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不能工作在阻塞模式,而是工作在阻塞模式可能在运行中会出现一些问题。
 
<think>好的,我现在要解决用户的问题,关于epollET(边缘触发)LT(水平触发)模式的区别,以及为什么在写聊天室的时候通常使用ET模式。首先,我需要回忆一下epoll的基本知识,然后深入理解ETLT的不同之处,最后结合聊天室的应用场景来分析原因。 首先,epoll是Linux下的一种I/O多路复用机制,用于高效处理大量文件描述符的事件。它有两种工作模式:LT(水平触发)ET(边缘触发)。这两种模式的主要区别在于事件通知的方式。 LT模式是默认的工作模式。当一个文件描述符就绪时,epoll_wait会通知应用程序,只要该文件描述符处于就绪状态(比如有数据可读),每次调用epoll_wait都会返回该事件,直到对应的条件不再满足。例如,如果socket接收缓冲区中有数据可读,每次调用epoll_wait都会触发读事件,直到缓冲区中的数据被全部读取完毕。 而ET模式则不同,它只在状态发生变化时通知一次。也就是说,只有当文件描述符从未就绪变为就绪时,才会触发事件通知。如果应用程序没有一次性处理完所有数据,之后即使缓冲区中还有数据,epoll_wait也不会再次通知,除非有新的数据到达导致再次状态变化。 现在需要分析为什么写聊天室的时候通常使用ET模式。首先,聊天室需要处理大量并发的连接,每个连接可能会有频繁的数据传输。ET模式的高效性在这里显得尤为重要。因为ET模式减少了事件被触发的次数,避免了重复通知,从而减少了不必要的系统调用上下文切换,提高了处理效率。 不过,使用ET模式需要应用程序负责一次性处理完所有数据,否则可能会丢失后续的事件通知。例如,当读取数据时,必须循环读取直到返回EAGAIN或EWOULDBLOCK错误,确保缓冲区中的数据被全部读取。这样虽然增加了代码的复杂性,但能更高效地利用系统资源。 而LT模式虽然编程更简单,因为它会持续通知直到条件不再满足,但在高并发场景下,频繁的事件通知可能导致性能下降。对于聊天室这种需要处理大量并发连接且每个连接数据量可能不大的情况,ET模式更适合,因为它减少了事件触发的次数,提高了整体的吞吐量。 此外,ET模式通常与非阻塞I/O结合使用。在ET模式下,必须确保文件描述符是非阻塞的,以避免在读取或写入时阻塞整个进程。这种组合使得程序能够更高效地处理多个连接,不会因为某个连接的阻塞操作而影响其他连接的处理。 总结来说,ET模式在聊天室中的应用主要是因为其高效性,能够减少系统调用次数,提升处理大量并发连接的能力。但这也要求开发者必须正确处理所有可能的数据,避免遗漏事件,确保程序的健壮性。 不过,我还需要验证一下这些观点是否正确。比如,ET模式是否真的在高并发下性能更好,或者是否存在其他因素需要考虑。可能需要查阅一些资料或案例来确认。例如,有些资料提到ET模式在需要高性能的场景下更优,但需要更谨慎的编程;而LT模式更适合简单的应用,或者对编程复杂度敏感的情况。 另外,关于聊天室的具体实现,ET模式可能更适用于需要快速响应高效处理的场景,例如实时通信,每个消息需要及时处理,而ET的事件驱动方式可以减少延迟。同时,由于聊天室通常使用长连接,保持大量并发连接,ET模式的高效处理能够更好地支持这种需求。 可能的误区是认为ET模式总是比LT快,但实际上正确性也很重要。如果处理不当,ET模式可能导致数据未被完全处理,从而引发问题。因此,正确的使用方式是非阻塞IO配合循环读写,确保处理所有可用数据。 最后,整理这些思路,确保回答结构清晰,逐步解释ETLT的区别,然后分析聊天室使用ET的原因,包括性能、并发处理、非阻塞IO的结合等,同时提醒需要注意的地方,比如正确处理数据。</think>### epollETLT模式区别及聊天室使用ET的原因 #### 1. **ET(边缘触发)与LT(水平触发)的核心区别** - **LT模式(默认)**: 只要文件描述符处于就绪状态(如缓冲区有数据可读),每次调用`epoll_wait`都会触发事件通知。 **示例**:若socket接收缓冲区中有100字节数据,未读完时,下次`epoll_wait`仍会提醒用户读取。 - **ET模式**: 仅在文件描述符状态**发生变化时触发一次**通知(如从“无数据”变为“有数据”)。若未处理完数据,后续`epoll_wait`不会重复通知。 **示例**:同样100字节数据,若未读完,即使剩余数据仍在缓冲区中,也不会再次触发读事件,除非有新数据到达。 #### 2. **为什么聊天室通常使用ET模式?** - **减少事件触发次数**: 聊天室需处理大量并发连接,ET模式仅在状态变化时触发一次通知,避免了LT模式下重复调用`epoll_wait`的开销,降低系统调用次数CPU占用。 - **提升吞吐量**: ET模式要求必须一次性处理完数据(如循环读取直到`recv`返回`EAGAIN`),避免因分多次处理导致延迟。这对实时性要求高的聊天室至关重要。 - **与非阻塞I/O结合更高效**: ET模式必须配合非阻塞文件描述符使用。在读取数据时,若缓冲区未读完,非阻塞IO会立即返回`EAGAIN`错误,避免阻塞线程,从而高效处理其他连接。 - **适用场景匹配**: 聊天室消息多为短文本,单次`recv`即可读完,适合ET的“一次性处理完”逻辑,避免LT频繁触发。 #### 3. **ET模式的注意事项** - **必须处理所有数据**: 若未完全读取缓冲区数据,剩余数据将无法被后续事件触发,需通过循环调用`recv`直到返回`EAGAIN`或`EWOULDBLOCK`。 - **必须使用非阻塞IO**: 若文件描述符为阻塞模式,未读完数据时`recv`会一直阻塞,导致线程卡死。 #### 4. **对比示例** - **LT模式处理流程**: ```c while (epoll_wait(...)) { if (事件为可读) { read(fd, buf, BUF_SIZE); // 可能未读完数据,下次epoll_wait仍会触发 } } ``` - **ET模式处理流程**: ```c while (epoll_wait(...)) { if (事件为可读) { while (1) { ret = read(fd, buf, BUF_SIZE); if (ret == -1 && errno == EAGAIN) break; // 非阻塞下读完所有数据 } } } ``` #### 5. **总结** ET模式通过减少事件通知次数强制高效处理数据,更适合高并发、低延迟的聊天室场景。但其编程复杂度更高,需确保正确处理所有数据非阻塞IO。LT模式更简单,但性能较低,适合对吞吐量要求不高的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值