IO 多路复用是通过一种机制,同时监视多个文件描述符,一旦某个文件描述符就绪(比如读就绪或写就绪),会通知应用程序进行相应的处理。 select、poll、epoll 本质上都是同步 IO,它们都需要在事件就绪后,由应用程序自己进行读写。而异步 IO 不需要应用程序自己进行读写,异步 IO 实现负责把数据从内核拷贝到用户空间。
Linux 提供 select、poll、epoll,来实现 IO 多路复用机制,下面将对它们进行简要的介绍。
select、poll
如果想了解 select、poll 的原型和详细说明,请点击该链接。这里只对 select 和 poll 的执行过程,进行简要的说明:
epoll 不通过轮询的方式来检查文件描述符。所有添加到 epoll 中的事件都会与设备驱动程序建立回调关系,当相应的事件发生时,回调函数负责把事件添加到就绪事件链表。当调用epoll_wait 检查是否有事件发生时,只需要检查就绪事件链表即可
epoll 有水平触发和边缘触发两种模式。水平触发是默认模式,边缘触发是高速模式。在水平触发模式下,如果一个描述符有数据可读,那么每次调用 epoll_wait 都会返回该描述符上发生的事件。而在边缘触发模式下,只会提醒一次,直到有新数据流入。因此在边缘触发模式下,读描述符一定要读空它的 buffer,即一直读到 read 的返回值小于它的请求值,或遇到 EAGAIN 错误(读非阻塞 socket 时,如果输入缓冲区中没有数据,那么会出现 EAGAIN 错误,意思是告诉应用程序再读一次或许就有数据了)。
当进程打开或新建文件时,内核会返回一个文件描述符,文件描述符是一个非负整数,取值范围是 0 到 max open files。
内核会为每个进程维护一个保存该进程打开的文件的记录表,文件描述符是这个记录表中的索引值,内核利用文件描述符来访问文件。
通常标准输入的文件描述符是 0,标准输出的文件描述符是 1,标准错误输出的文件描述符是 2。
下面主要介绍其中关于 epoll 的部分: