题目来源:字节跳动
作者:ORVR
select、poll、epoll说下详情和各自优缺点
这三个是目前常用的io复用模型,他们可以监视多个描述符,一旦某个描述符就绪(读或写就绪),能够通知程序进行相应的读写操作,但他们三个本质上都是同步io,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步io则无需自己负责进行读写,异步io的实现会负责把数据从内核拷贝到用户空间
select和poll类型,都是基于轮询机制,只不过select数据结构存放fd的数量有限制,一般默认不超过1024个,而poll的是一个链表结构,它没有最大文件描述符数量的限制
他们的缺点有
1.轮询的效率很低,每次都会都会有很多没有意义的遍历
2.内核需要将消息传递到用户空间,都需要内核拷贝动作,都需要维护一个用来存放大量fd的数据结构,使得用户空间和内核空间在传递该结构是复制开销大,这个开销会随着文件描述符的数量增加而线性增大
epoll基于操作系统支持的io通知机制,epoll支持水平触发和边沿触发两种模式
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
epolllt(水平触发)默认模式。只要该fd还有数据可读,每次epoll——wait都会返回它的事件,提醒用户程序去操作
epollet(边缘触发)
只会提示一次,直到下次再有数据流入之前都不会再提示,无论fd是否还有数据可读,所有在边缘模式下,read一个fd的时候一定要把它的buffer读完,即使读到read返回值小于请求值,或者遇到eagain错误,
epoll使用事件的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用内息回调机制激活该fd,epoll_wait便可以收到通知
EPOLLET触发模式的意义
若用EPOLLLT,系统中一旦有大量无需读写的就绪文件描述符,它们每次调用epoll_wait (opens new window)都会返回,这大大降低处理程序检索自己关心的就绪文件描述符的效率。 而采用EPOLLET,当被监控的文件描述符上有可读写事件发生时,epoll_wait会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait时,它不会通知你,即只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你。这比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符。
优点
- 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)
- 效率提升,不是轮询,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数 (opens new window) 即Epoll最大的优点就在于它只关心“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll
- 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
- epoll通过内核和用户空间共享一块内存来实现的
总结
select,poll,epoll都是IO多路复用机制,即可以监视多个描述符,一旦某个描述符就绪(读或写就绪),能够通知程序进行相应读写操作。 但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
- select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
- select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
- 连接数较少并且都很活跃,用select和poll效率更高
- 连接数较多并且都不很活跃,使用epoll效率更高