为什么epoll在level triggered模式下会触发多个read事件?

问题描述 投票:0回答:0

我正在使用 C 中的 epoll 编写一个状态机。我正在跟踪一个文件描述符,它是一个接受连接的 tcp 套接字,以及连接的文件描述符。在第一次读取和写入事件之后,epoll 发出多个

EPOLLIN
事件,这些事件将阻塞在
read
调用中。

这是代码现在的简化示例:

int main()
{
     // omitted socket, bind, listen, epoll_create for length
     num = epoll_wait(epollfd, events, maxev, -1);
     for (i = 0; i < num; i++)
     {
           struct epoll_event event = events[i];
           if (event.data.fd == sfd) 
           {
                 // accept and make socket non blocking
                 int cfd = accpet_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 
           {
                 if (event.events & EPOLLIN) 
                 {
                     // read from fd function called
                     event.events |= EPOLLOUT;
                     epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
                 }
                 if (event.events & EPOLLOUT) 
                 {
                     // write to fd function called
                     event.events |= EPOLLIN;
                     epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
                 }
           }
     }
     return 0;
}

read
返回0时,使用
EPOLL_CTL_DEL
从epoll实例中删除fd并关闭文件描述符。

我的问题是,在第一次写入发生后,epoll 会不断说我有一个读取事件。当我从文件描述符中读取时,-1 返回

EAGAIN
EWOULDBLOCK

使用的读取函数如下所示:

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)
     {
          // remove fd from epoll instance and close fd
     }
}

为了演示输出,服务器启动,客户端连接,客户端发送一条消息,客户端读取一条消息,然后客户端关闭连接。这是输出:

$ ./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 发出了多个“写”事件,并且程序因 SIGPIPE 而崩溃,因为客户端在收到一条消息后关闭了连接。

c server state-machine epoll
© www.soinside.com 2019 - 2024. All rights reserved.