epoll与边缘触发事件

bar*_*hen 4 c sockets linux epoll

epoll的手册页有一个边缘触发示例代码,如下所示:

for (;;) {
    nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_pwait");
        exit(EXIT_FAILURE);
    }

    for (n = 0; n < nfds; ++n) {
        if (events[n].data.fd == listen_sock) {
            conn_sock = accept(listen_sock,
                        (struct sockaddr *) &local, &addrlen);
            if (conn_sock == -1) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            setnonblocking(conn_sock);
            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = conn_sock;
            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                    &ev) == -1) {
                perror("epoll_ctl: conn_sock");
                exit(EXIT_FAILURE);
            }
        } else {
            do_use_fd(events[n].data.fd);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在函数do_use_fd中,我在while循环中调用非阻塞的recv直到EAGAIN,示例代码工作正常.

我对这个示例代码有疑问,假设现在我有50个套接字客户端连接,突然有10个客户端同时写入数据,所以epoll_wait()将返回10然后转到for循环:

for (n = 0; n < nfds; ++n)
Run Code Online (Sandbox Code Playgroud)

它将调用 do_use_fd(events[n].data.fd); 那10个客户端,假设n = 5已完成,并且n = 6尚未完成,突然事件n = 3的文件描述已经接收到新数据,在完成所有这10个事件并返回到epoll_wait之后,我会告诉我有一个客户有新数据要读取吗?或者我会错过它,因为当事件发生时,代码不在epoll_wait !!

cme*_*erw 9

只要你读到EAGAIN错误,你就会在下次打电话时得到这个事件epoll_wait.

仅当空和非空(或完全和非完全EPOLLOUT)之间发生更改时才会触发事件,但该状态将保持不变,直到事件通过epoll_wait.

在一个有点相关的说明:如果您注册EPOLLINEPOLLOUT事件并假设您从未填写发送缓冲区,您仍然会在每次触发EPOLLOUT返回的事件中设置标志- 请参阅https://lkml.org/lkml/2011/11/17/234有更详细的解释.epoll_waitEPOLLIN

最后,边缘触发模式的确切行为实际上取决于所使用的套接字类型,并且在任何地方都没有真正记录.我前段时间做了一些测试,并在此处记录了我的发现:http://cmeerw.org/blog/753.html#753 - 简而言之,对于数据报套接字,您可能会收到比预期更多的事件.