epoll下的ET和LT

LT模式:LT是epoll默认的工作方式,支持阻塞和非阻塞两种机制。LT模式下内核会持续通知你文件描述符就绪了,然后你可以对这个就绪的fd进行I/O操作。如果不做任何操作,内核还是会继续通知你的。
ET模式:ET模式相对LT模式更加高效,只支持非阻塞模式。在这个模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不再为那个文件描述符发生更多的就绪通知。直到你做了某些操作导致那个文件描述符不再为就绪状态了。
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式时,必须使用非阻塞套接口,以避免一个文件句柄的阻塞导致把其他文件描述符饿死。
ET模式下,只会触发一次读事件,如果不循环读取,除非新的连接到来,其它未被读入的链接不会触发IO事件
 
socket 可读事件水平模式(LT)触发条件:
1. socket上无数据 => socket上有数据
2. socket处于有数据状态
socket 可读事件边缘模式(ET)触发条件:
1. socket上无数据 => socket上有数据
2. socket又新来一次数据
 
简单总结:
LT 模式下,读事件触发后,可以按需收取想要的字节数,不用把本次接收到的数据收取干净(即不用循环到 recv 或者 read 函数返回 -1,错误码为 EWOULDBLOCK 或 EAGAIN);ET 模式下,读事件必须把数据收取干净,因为你不一定有下一次机会再收取数据了,即使有机会,也可能存在上次没读完的数据没有及时处理,造成客户端响应延迟。
LT 模式和 ET 模式各有优缺点,无所谓孰优孰劣。使用 LT 模式,我们可以自由决定每次收取多少字节(对于普通 socket)或何时接收连接(对于侦听 socket),但是可能会导致多次触发;使用 ET 模式,我们必须每次都要将数据收完(对于普通 socket)或必须理解调用 accept 接收连接(对于侦听socket),其优点是触发次数少。
 
EPOLL的内核实现:
ET和LT只是在从 rdlist中返回的时候有区别,内核首先会将rdlist拷贝到一个临时链表txlist, 然后如果是LT事件并且事件就绪的话fd被重新放回了rdllist。那么下次epoll_wait当然会又把rdllist里的fd拿来拷给用户了。
 
需要注意的是上面LT fd拷贝回rdlist并不是向用户处理完之后发生的,而是向用户拷贝完之后直接复制到rdlist中,那么如果用户消费这个事件使事件不就绪了怎么办,比如说本来是可读的,返回给用户,用户读到不可读为止,继续调用epoll_wait 返回rdlist,则发现不可读,事实上每次返回之前会以NULL继续调用poll,判断事件是否变化,平时调用poll会传递个poll_table变量,就进行添加到等待队列中,而此时不需要添加,只是判断一下状态,如果rdlist中状态变化了,就不会给用户返回了。
 
epoll每次都将txlist中的LT事件不等用户消费就直接返回给rdlist,那么在用户消费了该事件后,导致事件不就绪,再次调用epoll_wait,epoll_wait还会返回rdlist吗?
不会再次返回,因为在返回就绪列表之前会还调用一次revents = epi->ffd.file->f_op->poll(epi->ffd.file, NULL) 来判断事件,如果事件发生了变化,就不在返回。
 
ET模式触发方式:
根据对两种加入rdlist途径的分析,可以得出ET模式下被唤醒(返回就绪)的条件为:
对于读取操作:
(1) 当buffer由不可读状态变为可读的时候,即由空变为不空的时候。
(2) 当有新数据到达时,即buffer中的待读内容变多的时候。
(3) 当buffer中有数据可读(即buffer不空)且用户对相应fd进行epoll_mod IN事件时
对于写操作:
(1) 当buffer由不可写变为可写的时候,即由满状态变为不满状态的时候。
(2) 当有旧数据被发送走时,即buffer中待写的内容变少得时候。
(3) 当buffer中有可写空间(即buffer不满)且用户对相应fd进行epoll_mod OUT事件时
对于LT模式则简单多了,除了上述操作为读了一条事件就绪就一直通知。
 
实际应用:
当epoll工作在ET模式下时,对于读操作,如果read一次没有读尽buffer中的数据,那么下次将得不到读就绪的通知,造成buffer中已有的数据无机会读出,除非有新的数据再次到达。对于写操作,主要是因为ET模式下fd通常为非阻塞造成的一个问题——如何保证将用户要求写的数据写完。
要解决上述两个ET模式下的读写问题,我们必须实现:
a. 对于读,只要buffer中还有数据就一直读;
b. 对于写,只要buffer还有空间且用户请求写的数据还未写完,就一直写。
使用这种方式一定要使每个连接的套接字工作于非阻塞模式,因为读写需要一直读或写直到出错(对于读,当读到的实际字节数小于请求字节数时就可以停止),而如果你的文件描述符如果不是非阻塞的,那这个一直读或一直写势必会在最后一次阻塞。这样就不能在阻塞在epoll_wait上了,造成其他文件描述符的任务饿死。
所以也就常说“ET需要工作在非阻塞模式”,当然这并不能说明ET不能工作在阻塞模式,而是工作在阻塞模式可能在运行中会出现一些问题。
 
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值