在Muduo网络库中同时使用了非阻塞socket与epoll,在此简单梳理下。
非阻塞sokcet和epoll共同工作的过程主要涉及网络编程中的非阻塞I/O和事件驱动机制。下面将详细解释这两者如何协同工作:
非阻塞socket简介
- 在传统的阻塞socket编程中,当调用如
read
、write
、accept
等函数时,如果当前没有数据可读或没有空间可写,线程或进程会被挂起,等待数据到来或空间可用。这可能导致资源(如CPU时间)的浪费。 - 非阻塞socket则不同,它允许socket调用立即返回,即使当前没有数据可读或没有空间可写。这意味着线程或进程不会被挂起,可以继续执行其他任务。
- 非阻塞socket通过设置socket的选项(如使用
fcntl
函数设置O_NONBLOCK
标志)来实现。
epoll简介
- epoll是Linux内核提供的一种高效的事件通知机制,用于监控多个文件描述符(如socket)的状态变化。
- 与传统的select/poll机制相比,epoll具有更高的效率和可扩展性。它使用一种称为“事件驱动”的方式工作,即当某个文件描述符的状态发生变化时,内核会主动通知应用程序。
- epoll有两种触发模式:水平触发(Level Triggered, LT)和边缘触发(Edge Triggered, ET)。在LT模式下,只要文件描述符的状态未改变,应用程序每次调用epoll_wait都会收到通知;而在ET模式下,应用程序只会收到一次通知,需要在状态改变后主动读取或写入数据。
非阻塞socket与epoll共同工作过程
- 当使用非阻塞socket编程时,为了避免线程或进程被挂起,通常会结合使用epoll来监控socket的状态变化。
- 具体而言,应用程序首先会将socket设置为非阻塞模式,并将其注册到epoll的监控列表中。
- 当socket的状态发生变化(如有数据可读或可写)时,内核会通过epoll通知应用程序。此时,应用程序可以调用相应的socket函数(如
read
、write
)来处理数据,而不用担心被挂起。 - 由于socket处于非阻塞模式,因此这些函数会立即返回。如果当前没有数据可读或没有空间可写,函数会返回一个错误码(如
-1
),并设置相应的errno值(如EWOULDBLOCK
)。应用程序可以根据这些错误码来判断是否需要继续等待数据或执行其他任务。 - 通过这种方式,非阻塞socket与epoll共同实现了高效的网络编程模型,使得应用程序能够充分利用系统资源,提高程序的性能和响应速度。
此图是IO复用(IO multiplexing)模型图。epoll属于IO复用的一种。图中下方的read可以是阻塞IO,也可是本文讲的非阻塞IO。
需要注意的是,当使用非阻塞socket和epoll的ET模式结合进行编程时,需要特别注意数据的完整性和连接的稳定性。
例如,在读取数据时,可能需要循环调用read
函数以确保读取到完整的数据包,在Muduo库中Buffer的readFd方法封装了readv系统调用,可以最多一次读取64kbyte的数据。在关闭连接时,也需要确保对方已经接收到了所有的数据并关闭了连接。这些都需要在应用程序中进行适当的处理。
非阻塞+epoll是异步IO吗
在这种情况下,虽然非阻塞socket和epoll允许应用程序在等待I/O操作完成时不被阻塞,但它们本身并不直接等同于异步I/O(Asynchronous I/O)。澄清一下这两个概念:
-
非阻塞I/O:非阻塞I/O意味着当应用程序尝试执行一个I/O操作时,如果操作不能立即完成(例如,没有数据可读或缓冲区已满),则调用会立即返回一个错误(通常是
EWOULDBLOCK
),而不是将调用线程挂起。这样,应用程序可以继续执行其他任务,稍后再次尝试该I/O操作。 -
异步I/O:异步I/O更进一步。它允许应用程序启动一个I/O操作,然后立即继续执行其他任务,而不必等待该操作完成。当I/O操作完成时,系统会通知应用程序(通常是通过一个回调函数或信号)。这样,应用程序可以真正并行地执行多个任务,而不仅仅是轮询I/O操作的状态。
就如Muduo库中,使用非阻塞socket和epoll实现的是基于事件驱动的反应式编程模型,而不是真正的异步I/O。应用程序需要定期调用epoll_wait
来检查是否有任何已注册的文件描述符(如socket)的状态发生了变化。当检测到变化时,应用程序会执行相应的处理逻辑。
然而,有些系统调用(如Windows的IOCP或Linux的aio系列函数)提供了真正的异步I/O支持。应用程序可以提交一个I/O请求,并提供一个回调函数,当I/O操作完成时,系统会调用该回调函数。这样,应用程序可以完全避免轮询,从而实现更高的效率和更好的响应性。
相关概念也可移步另一篇博客【IO的阻塞和非阻塞浅析】细看。