I/O多路复⽤通常通过select、poll、epoll等系统调⽤来实现。
select: select是⼀个最古老的I/O多路复⽤机制,它可以通过轮询的方式监视多个⽂件描述符的可读、可写和错误状态。然而,但是它的效率可能随着监视的⽂件描述符数量的增加⽽降低。
触发方式:水平触发
(单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量;(在linux内核头文件中定义:#define _FD_SETSIZE 1024)
poll: poll是select的⼀种改进,它也使⽤轮询⽅式来检查多个⽂件描述符的状态,但是 poll() 没有最大文件描述符数量的限制。但对于⼤量的⽂件描述符,poll的性能也可能变得不够高效。
触发方式:水平触发
epoll: epoll是Linux特有的I/O多路复⽤机制,相较于select和poll,它在处理大量⽂件描述符时更加⾼效。 epoll使⽤事件通知的⽅式,只有在⽂件描述符就绪时才会通知应⽤程序,而不需要应⽤程序轮询。 I/O多路复⽤允许在⼀个线程中处理多个I/O操作,避免了创建多个线程或进程的开销,允许在⼀个线程中处理多个 I/O操作,避免了创建多个线程或进程的开销。
触发方式:水平触发 or 边缘触发 (默认水平)
触发模式
-
边缘触发(Edge Triggered, ET) 在这种模式下,当一个文件描述符满足特定条件(例如,有可读数据)时,epoll_wait()会返回该文件描述符。然后,应用程序需要立即处理这个事件。如果处理不完全(例如,只读取了一部分数据),那么epoll将不会再次报告该事件,直到下次满足条件时才会报告。这种方式有助于提高系统的效率,因为它减少了epoll调用的次数。
-
水平触发(Level Triggered, LT) 在这种模式下,每当一个文件描述符满足特定条件时,epoll_wait()都会返回该文件描述符。这意味着,即使应用程序已经处理了某个事件,只要该事件仍然存在,epoll就会持续报告它。这种方式更易于编程,因为程序员不需要担心遗漏任何事件。
区别:
- 支持的最大连接数: select和poll支持的最大连接数受到系统限制,通常较小;而epoll支持更大的连接数,可以在1GB内存的机器上处理大约10万个连接。
- I/O效率: 随着连接数的增加,select和poll的I/O效率会出现明显的下降,这是因为它们需要遍历所有连接以确定哪些连接已经准备好进行读写操作。而epoll通过回调机制实现了更高的I/O效率,因为只有那些准备好的连接才会触发回调函数。
- 消息传递方式: select和poll需要将消息从用户空间传递到内核空间,并在事件发生时将其返回到用户空间,这涉及到大量的内存拷贝和上下文切换。而epoll则使用了一种共享内存的方法,使得内核可以直接将数据传输给用户空间。