You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

水平触发模式下epoll多次触发EPOLLIN事件的原因排查

基于epoll实现状态机时,EPOLLIN持续触发导致read返回EAGAIN的问题

我用C语言基于epoll实现状态机,跟踪用于接收连接的TCP监听套接字文件描述符,以及已建立连接的文件描述符。目前遇到一个异常情况:首次读写事件完成后,epoll会多次触发本应阻塞的EPOLLIN事件,此时调用read会返回-1,错误码为EAGAIN或EWOULDBLOCK。

简化代码示例

主函数逻辑

int main()
{
     // 省略socket、bind、listen、epoll_create相关初始化代码
     int num = epoll_wait(epollfd, events, maxev, -1);
     for (int i = 0; i < num; i++)
     {
           struct epoll_event event = events[i];
           if (event.data.fd == sfd) 
           {
                 // 接受连接并将套接字设置为非阻塞模式
                 int cfd = accept_connection(event.data.fd);
                 struct epoll_event ee = { 0 };
                 ee.data.fd = cfd;
                 ee.events = EPOLLIN;
                 epoll_ctl(epollfd, EPOLL_CTL_ADD, cfd, &ee);
           }
           else 
           {
                 int fd = event.data.fd;
                 if (event.events & EPOLLIN) 
                 {
                     // 调用从fd读取数据的read_from_fd函数
                     event.events |= EPOLLOUT;
                     epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
                 }
                 if (event.events & EPOLLOUT) 
                 {
                     // 调用向fd写入数据的函数
                     event.events |= EPOLLIN;
                     epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
                 }
           }
     }
     return 0;
}

注:当read返回0(客户端关闭连接)时,会通过EPOLL_CTL_DEL将该fd从epoll实例中移除并关闭文件描述符。

读取数据函数

ssize_t read_from_fd(int fd)
{
     ssize_t n;
     printf("read called\n");
     n = read(fd, buf, MAX_BUF);
     if (n == -1)
     {
          if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) 
          {
              printf("read blocked\n");
          }
          return -1;
     }
     if (n == 0)
     {
          // 从epoll实例移除fd并关闭fd的逻辑
     }
     return n;
}

现象展示

启动服务器后,客户端执行连接、发送一条消息、读取一条消息后关闭连接,服务器输出如下:

$ ./server
accepted connection <fd>
read called
write called
read called
read blocked
read called
closed connection with <fd>

注:有时会出现多条read called和read blocked日志,数量从1到约25不等。

关联问题

如果移除写操作完成后调用的epoll_ctl修改事件的代码,epoll会多次触发EPOLLOUT事件,且客户端接收消息后关闭连接会导致程序触发SIGPIPE信号崩溃。

内容的提问来源于stack exchange,提问作者boreddad420

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:52:14