Epoll常见面试问题,LT和ET区别?将socket设为非阻塞?正确的读写方式?关于epoll的数据结构?epoll与select、poll的对比?

epoll——常见面试问题

epoll——常见面试问题

常问面试问题

1、Linux epoll ET模式下 EPOLLOUT和EPOLLIN触发场景?

1、EPOLLOUT事件:

EPOLLOUT事件只有在连接时触发一次,表示可写,其他时候想要触发,那你要先准备好下面条件:

  • 1.某次write,写满了发送缓冲区,返回错误码为EAGAIN。

  • 2.对端读取了一些数据,又重新可写了,此时会触发EPOLLOUT。

简单地说:EPOLLOUT事件只有在不可写到可写的转变时刻,才会触发一次,所以叫边缘触发,这叫法没错的!

其实,如果你真的想强制触发一次,也是有办法的,直接调用epoll_ctl重新设置一下event就可以了,event跟原来的设置一模一样都行(但必须包含EPOLLOUT),关键是重新设置,就会马上触发一次EPOLLOUT事件。

2、EPOLLIN事件:

EPOLLIN事件则只有当对端有数据写入时才会触发,所以触发一次后需要不断读取所有数据直到读完EAGAIN为止。否则剩下的数据只有在下次对端有写入时才能一起取出来了。

现在明白为什么说epoll必须要求异步socket了吧?如果同步socket,而且要求读完所有数据,那么最终就会在堵死在阻塞里。

2、epoll的ET模式下,正确的读写方式

  • 读: 只要可读, 就一直读,直到返回0,或者 errno = EAGAIN或者EWOULDBLOCK
  • 写:只要可写, 就一直写,直到数据发送完,或者 errno = EAGAIN或者EWOULDBLOCK

从字面上看, 意思是:

  • EAGAIN: 再试一次
  • EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block
正确的读:
while ((byte_read = read(fd, buf + n, BUFSIZ-1)) > 0) {
    n += byte_read;//计算读取的字节
}
if (nread == -1 && errno != EAGAIN) {
    perror("read error");
}


正确的写:
int nwrite, data_size = strlen(buf);  
n = data_size;  
while (n > 0) {  
    nwrite = write(fd, buf + data_size - n, n);  
    if (nwrite < n) {  
        if (nwrite == -1 && errno != EAGAIN) {  
            perror("write error");  
        }  
        break;  
    }  
    n -= nwrite;  
}

3、epoll的水平触发和边沿触发区别

(1)水平触发LT:缺省的工作方式(epoll默认的设置),并且同时支持block和no-block socket.

  • 接受缓冲区不为空,对应fd一直处于“读就绪”状态;发送缓冲区不满,对应fd一直处于“写就绪”状态(关于这里的缓冲区:读多少就少多少,写多少就多多少)。所以水平触发,只要数据没读完,那么在epoll_wait中,就一直认为该fd处于就绪状态。
  • 采用LT模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理此事件,当下一次调用epoll_wait是,epoll_wait还会将此事件通告应用程序。
  • 如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率

(2)边沿触发ET:只支持no-block socket

如果套接口被设置成阻塞模式,服务器就会一直阻塞在accept调用上,直到其他某个客户建立一个新的连接为止。但是在此期间,服务器单纯地阻塞在accept 调用上,就绪队列中的其他描述符都得不到处理。

  • 当fd对应的读缓冲区从”有“变为了”无“时,对应fd才处于”读就绪“。当fd对应的写缓冲区从”无“变为了”有“时,对应fd才处于”写就绪“。注意:一定是在”有“和”无“之间变化才能触发
  • 当epoll_wait检测到其上有事件发生并将此通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait调用将不在向应用程序通知这一事件。
  • ET模式降低了同意epoll事件被触发的次数,效率比LT模式高。

4、使用epoll时需要将socket设为非阻塞吗?

只有边沿触发才必须设置为非阻塞。

边沿触发的问题:

  • 1、sockfd 的边缘触发,高并发时,如果没有一次处理全部请求,则会出现客户端连接不上的问题。不需要讨论 sockfd 是否阻塞,因为epoll_wait() 返回的必定是已经就绪的连接,所以不管是阻塞还是非阻塞,accept() 都会立即返回。
  • 2、阻塞 connfd 的边缘触发,如果不一次性读取一个事件上的数据,会干扰下一个事件,所以必须在读取数据的外部套一层循环,这样才能完整的处理数据。但是外层套循环之后会导致另外一个问题:处理完数据之后,程序会一直卡在 recv() 函数上,因为是阻塞 IO,如果没数据可读,它会一直等在那里,直到有数据可读。但是这个时候,如果用另一个客户端去连接服务器,服务器就不能受理这个新的客户端了。
  • 3、非阻塞 connfd 的边缘触发,和阻塞版本一样,必须在读取数据的外部套一层循环,这样才能完整的处理数据。因为非阻塞 IO 如果没有数据可读时,会立即返回,并设置 errno。这里我们根据 EAGAIN 和 EWOULDBLOCK 来判断数据是否全部读取完毕了,如果读取完毕,就会正常退出循环了。

总结一下:

  • 1、对于监听的 sockfd,最好使用水平触发模式,边缘触发模式会导致高并发情况下,有的客户端会连接不上。如果非要使用边缘触发,可以用 while 来循环 accept()。
  • 2、对于读写的 connfd,水平触发模式下,阻塞和非阻塞效果都一样,建议设置非阻塞。
  • 3、对于读写的 connfd,边缘触发模式下,必须使用非阻塞 IO,并要求一次性地完整读写全部数据。

所以在用EPOLL的时候,我们都用fcntl将描述符置为非阻塞吧,皆大欢喜。

5、ET模式下的accept问题

请思考以下一种场景:在某一时刻,有多个连接同时到达,服务器的 TCP 就绪队列瞬间积累多个就绪连接,由于是边缘触发模式,epoll 只会通知一次,accept 只处理一个连接,导致 TCP 就绪队列中剩下的连接都得不到处理。在这种情形下,我们应该如何有效的处理呢?

解决的方法是:解决办法是用 while 循环抱住 accept 调用,处理完 TCP 就绪队列中的所有连接后再退出循环。如何知道是否处理完就绪队列中的所有连接呢? accept 返回 -1 并且 errno 设置为 EAGAIN 就表示所有连接都处理完。

6、epoll涉及的数据结构?epoll与select、poll的对比?
篇幅过长,参考上一篇博客,便于清楚理解

&lt;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的结合等,同时提醒需要注意的地方,比如正确处理数据。&lt;/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模式更简单,但性能较低,适合对吞吐量要求不高的场景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值