「IO 模型」IO 多路复用技术实现对比2
Hello,大家好!我是七寸君。
epoll
基本原理
epoll 是在 2.6 内核中提出的,是之前的 select 和 poll 的增强版本。相对于 select 和 poll 来说,epoll 更加灵活,没有描述符限制。epoll 使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的 copy 只需一次。
epoll 支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些 fd 刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll 使用“事件”的就绪通知方式,通过 epoll_ctl 注册 fd,一旦该 fd 就绪,内核就会采用类似 callback 的回调机制来激活该 fd,epoll_wait 便可以收到通知。
epoll 对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT 模式是默认模式,LT 模式与 ET 模式的区别如下:
> LT 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。
> ET 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。
1. LT 模式
LT(level triggered) 是缺省的工作方式,并且同时支持 block 和 no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的。
2. ET 模式
ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 作 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
ET模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比LT模式高。epoll 工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
3. 在 select/poll 中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而 epoll 事先通过 epoll_ctl() 来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似 callback 的回调机制,迅速激活这个文件描述符,当进程调用 epoll_wait() 时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是 epoll 的魅力所在。)
优劣对比
1. 没有最大并发连接的限制,能打开的 FD 的上限远大于 1024(1G的内存上能监听约10万个端口)。
2. 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的 FD 才会调用 callback 函数;即 epoll 最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll 的效率就会远远高于 select 和 poll。
3. 内存拷贝,利用 mmap() 文件映射内存加速与内核空间的消息传递;即 ,epoll 使用 mmap 减少复制开销。
如果没有大量的 idle-connection 或者 dead-connection,epoll 的效率并不会比 select/poll 高很多,但是当遇到大量的 idle-connection,就会发现 epoll 的效率大大高于 select/poll。因为其只关注“活跃”的链接。
总结
支持一个进程所能打开的最大连接数
FD 剧增后的带来的 IO 效率问题
消息传递方式
综上,在选择 select、poll、epoll 时要根据具体的使用场合以及这三种方式的自身特点:
1. 表面上看 epoll 的性能最好,但是在连接数少并且连接都十分活跃的情况下,select 和 poll 的性能可能比 epoll 好,毕竟 epoll 的通知机制需要很多函数回调。
2. select 低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。
- EOF -
关注「七寸知架构」加星标,不错过技术那些事儿
❤️ 您的 点赞 和 在看 就是最大的支持❤️