IO多路复用EPOLL的ET模式下EPOLLIN事件触发时机

本文详细探讨了在客户端发送新数据时,单线程和多线程环境下,使用Epoll结合Nonblock IO的不同场景及行为。通过实验验证,阐述了在不同条件下Epoll_wait的返回情况,包括读事件触发条件、线程读取速度对事件感知的影响等关键信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网上说的太宏观,自己测试一下各种情况,FD为nonblock。

0.客户端发送新数据过来,单线程阻塞在epoll_wait,epoll_wait返回可读事件。

1.单线程阻塞在epoll_wait,客户端发送新数据过来,epoll_wait返回可读事件。

2.单线程阻塞在epoll_wait,客户端发送新数据过来,epoll_wait返回可读事件开始读数据,同时客户端不再发送数据过来,并且缓冲区的数据没有读完,线程继续epoll_wait,不会返回可读事件。

3.单线程阻塞在epoll_wait,客户端发送新数据过来,epoll_wait返回可读事件开始读数据,同时客户端继续发送新数据过来,并且缓冲区的数据循环读取直到客户端不发了,线程继续epoll_wait,不会返回可读事件。

4.单线程阻塞在epoll_wait,客户端发送新数据过来,epoll_wait返回可读事件开始读数据,同时客户端不再发送数据过来,并且缓冲区的数据没有读完,线程继续epoll_wait,此时客户端继续发送新数据过来返回可读事件。

5.单线程阻塞在epoll_wait,客户端发送新数据过来,epoll_wait返回可读事件开始读数据,同时客户端不再发送数据过来,并且缓冲区的数据没有读完,但是用EPOLL_CTL_MOD重新绑定客户端FD的EPOLLIN | EPOLLET到EPOLL_FD上面,线程继续epoll_wait,返回可读事件。

6.多线程阻塞在epoll_wait,客户端一次性发送新数据过来(理想情况)一个线程返回可读事件。

7.多线程阻塞在epoll_wait,客户端一次性发送新数据过来(理想情况)一个线程返回可读事件,该线程开始缓慢读数据,同时客户端继续一次性发送新数据过来,另一个线程返回可读事件。

8.多线程阻塞在epoll_wait,客户端一次性发送新数据过来(理想情况)一个线程返回可读事件,该线程开始快速读数据,同时客户端继续一次性发送新数据过来,另一个线程不会返回可读事件。

9.多线程阻塞在epoll_wait,客户端分批次发送新数据过来(实际情况有缓冲区大小,网络拥塞等原因限制导致分批)多个线程返回可读事件。

总结:有新数据来或者进行epoll_ctl( 缓冲区有数据剩余)会触发读事件,第8点比较奇怪,原因可能是线程读数据太快了,其他线程感知不到缓冲区来了新数据。

### 使用 epoll 实现 I/O 多路复用的技术详解 #### 1. 创建 epoll 文件描述符 `epoll_create` 函数用于创建一个新的 epoll 实例,并返回一个文件描述符。此函数的参数 `size` 是提示内核需要监控的大致文件描述符数量,但在现代 Linux 版本中已经不再重要[^2]。 ```c #include <sys/epoll.h> int epfd = epoll_create(1024); if (epfd == -1) { perror("epoll_create"); } ``` #### 2. 添加或修改感兴趣的事件 使用 `epoll_ctl` 将需要监视的文件描述符注册到 epoll 实例上。可以通过设置不同的操作标志(如 `EPOLL_CTL_ADD`, `EPOLL_CTL_MOD`, 和 `EPOLL_CTL_DEL`)来管理这些文件描述符及其对应的事件类型。 ```c struct epoll_event ev; ev.events = EPOLLIN | EPOLLET; // 设置读取事件以及边缘触发模式 ev.data.fd = client_fd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &ev) == -1) { perror("epoll_ctl: add"); } ``` 此处需要注意的是,可以配置多种类型的事件,比如可写 (`EPOLLOUT`) 或异常条件 (`EPOLLERR|EPOLLHUP`) 等[^3]。 #### 3. 阻塞等待事件发生 调用 `epoll_wait` 来阻塞当前线程直到有事件准备就绪或者超时时间到达为止。该方法会将所有已准备好处理的事件填充至用户空间缓冲区供后续访问。 ```c #define MAX_EVENTS 64 struct epoll_event events[MAX_EVENTS]; int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); for(int i=0;i<nfds;i++) { if(events[i].events & EPOLLIN){ handle_read(events[i].data.fd); } else if(events[i].events & EPOLLOUT){ handle_write(events[i].data.fd); } } ``` 上述代码片段展示了如何循环遍历由 `epoll_wait` 返回的结果列表,并依据具体发生的事件执行相应的回调逻辑[^4]。 #### 性能优势分析 相比传统的 select/poll 方法,epoll 的主要性能改进在于其无需每次轮询都重新扫描所有的句柄状态,而是仅需关注那些真正发生变化的部分。这使得即使存在大量的非活动连接也不会造成显著的 CPU 负载增加。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值