epoll 的边缘触发为什么一定要搭配非阻塞 I/O?

epoll 是 linux 系统中提供的多路复用机制,现代服务器高并发的秘诀就在于使用 epoll。相较于 select 和 poll,epoll 额外提供了边缘触发模式

  • 水平触发(LT):只要套接字关联的内核缓冲区有数据可读写,就会返回
  • 边缘触发(ET):仅当套接字关联的内核缓冲区有新的数据到达,才会返回

目前大家普遍认为 ET 模式要比 LT 模式更高效,原因在于 ET 能减少系统调用的次数仅当新数据到达时才返回。在使用 ET 时,我们必须确保每次 epoll_wait 返回后,都将内核缓冲区中的数据读取干净,避免数据丢失。

我们考虑如下问题来思考 ET 是否真的比 LT 更高效!
问题:如果 LT 模式下,我们确保每次 epoll_wait 返回时都将内核缓冲区中的数据读取干净,那么 LT 模式所需要的系统调用次数不是和 ET 一样多吗(仅在有新数据到达时才返回)?
答案:是的,在这种情况下 LT 和 ET 的效率是差不多的,但是 LT 在编程时,需要在写事件完成后移除对写事件的检测(否则 epoll_wait 会一直报可写),相对麻烦

接下来分情况讨论

  1. 内核缓冲区数据小于用户缓存区大小
    • LT + 阻塞 I/O:一次 read
    • LT + 非阻塞 I/O:一次 read
    • ET + 阻塞 I/O:一次 read
    • ET + 非阻塞 I/O:一次 read
  2. 内核缓冲区数据大于用户缓存区大小
    • LT + 阻塞 I/O:一次 read,然后 epoll_wait 又立即返回,继续 read,循环往复至读完内核缓冲区数据
    • LT + 非阻塞 I/O:读事件与 ET + 非阻塞 I/O 是一样的,但是对于写事件,将数据写完后,必须及时移除对写事件的检测,否则,每次 epoll_wait 都会返回(可写)
    • ET + 阻塞 I/O:while(true) 中反复调用 read,但读完后无法主动跳出循环(read 读不到数据,被阻塞),就行卡住了一样
    • ET + 非阻塞 I/O:while(true) 中反复调用 read,如果返回 -1 且 errno 为 EAGAIN 或 EWOULDBLOCK 则代表读完,可跳出循环

总结:

  1. epoll 边缘触发必须搭配非阻塞 I/O 的原因在于,ET 模式要求必须将内核缓冲区数读完,而阻塞 I/O 在读完后无法跳出循环
  2. 性能差异不在于 ET 或 LT,而在于阻塞 I/O 和非阻塞 I/O,只要保证使用非阻塞 I/O,且每次都将内核缓冲区数据读干净,那么二者性能就是一样的(epoll_wait 系统调用次数一致)。只不过 LT 在编程实现上需要对写事件特殊处理,显得比较鸡肋罢了。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值