原文:https://blog.csdn.net/weixin_45743893/article/details/122970342
IO多路复用概念
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。
其实就是在单个线程中通过记录跟踪每一个I/O流的状态来管理多个I/O流。
它们三的作用
提供一种IO复用的方式。即让单个进程可以监视多个文件描述符,一旦某个fd就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。
fd是什么?
- fd:文件描述符
文件描述符(file descriptor)是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。
select
select基于fd_set结构体(一个long类型数组),数组内的每一个元素都与一个fd相关联。select是基于遍历来查找fd事件的,所以时间复杂度为O(n)。
select在监听过程中,每次需要把fd列表从用户态(用户空间?)拷贝到内核态,然后再遍历所有fd,判断有无读写事件发生(这个流程每次调用select都要进行)。这也导致select在监听IO数量较多的情况下,性能开销极大(poll也有这个缺点)
为了减少数据拷贝带来的性能损坏,所以内核对单个进程可监视的fd数量做了限制。
水平触发:如果用户程序没有处理select所报告的fd,则下一次select时会继续报告此fd。
poll
poll与select的机制基本一致。由于poll是基于链表存储fd关联的,所以poll没有最大连接数限制。poll也是基于遍历来查找fd事件的,时间复杂度也是O(n)。
水平触发:如果用户程序没有处理poll所报告的fd,则下一次poll时会继续报告此fd。
poll的缺点:
和select一样,每次都把全部fd拷贝进内核态,再从中遍历查找有新事件的fd,性能开销大。
epoll
epoll底层是基于哈希表和回调函数的,所以时间复杂度为O(1)。
epoll有两种模式,LT和ET,LT是默认的模式,ET是高速模式。
- LT模式(水平触发):epoll_wait检测到某fd事件就绪并通知用户程序时,如果用户程序不处理该事件,则每次epoll_wait都会返回这个fd事件,不断提醒用户程序去操作。
- ET模式(边缘触发):当一个fd里有新的数据变化时,epoll只会向用户程序返回一次报告,直到下次再有新的数据流入之后,才会再次返回报告,无论fd中是否还有数据可读。
epoll的优点:
不会像select或poll那样因为打开的fd过多而影响性能。
没有最大并发限制连接限制。
epoll在监听到fd变化后不必像select或poll那样返回整个fd列表来进行遍历查找,而是只将产生变化的fd(即活跃的fd)放入一个列表中,调用callback函数返回。
使用了mmap技术,利用mmap()文件映射内存加速与内核空间的消息传递。
epoll存在的问题:
当活跃连接数过多时可能会有性能问题。
epoll机制需要很多回调函数,在连接数较少的情况下,性能可能不如select和poll。