如何检测空的epoll集

时间:2013-04-18 06:08:56

标签: c linux asynchronous epoll

我正在学习使用epoll,我写了以下示例

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>

int main() {
    int epfd;
    struct epoll_event ev;
    struct epoll_event ret;
    char buf[200];
    int n,k,t;

    epfd = epoll_create(100);
    assert(0 ==
            fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK)
          );

    ev.data.fd = 0;
    ev.events = EPOLLIN  | EPOLLET;

    if(epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &ev) != 0)
        perror("epoll_ctl");


    while((n = epoll_wait(epfd, &ret, 1, -1)) > 0) {
        printf("tick!\n");

        if(ret.data.fd == 0) {
            k=0;
            while((t=read(0, buf, 100)) > 0) {
                k+=t;
            }   

            if(k == 0) {
                close(0);
                printf("stdin done\n");
            }
        }   
    }

    perror("epoll");
    return 0;
}

如果您尝试在终端中运行它将无法正常工作,因为fds 0,1和2都指向相同的打开文件,因此close(0)不会从epoll集中删除stdin。你可以通过“cat | ./a.out”解决这个问题。我知道很脏的技巧,但设置一个带有命名管道或套接字的小例子会更复杂。

现在,一切正常,文件从epoll集中删除,但是下一个epoll_wait调用会永久阻塞,因为它是空集!所以我需要检测epoll文件描述符(epfd)是否是一个空的epoll集。

我该如何解决这个问题? (一般来说,不只是在stdin完成时调用exit) 谢谢!

2 个答案:

答案 0 :(得分:0)

基本上,如果你正确使用epoll,那么你应该永远不会出现意外空的epoll集。你应该知道什么时候还有更多。好吧,或者至少这是理论。让我来看看:

你在这里使用EPOLLET(一般来说,这是正确的思考)。这意味着文件描述符0在&ret中返回时将从epoll中删除。此时你应该通过从0读取一些数据来处理它,然后通过再次将文件描述符0添加到epoll中来“重新布防”它(除非它当然是关闭的)。有关这应该如何工作的示例,请删除内部循环,然后执行:

k = read(0, buf, 100);

最多读取100个字节。这个想法是,如果你管道一个大于那个文件,它应该在整个循环中进行几次。为了使这项工作,如果k> 0,处理k个字节后,需要再次调用epoll_ctl(..EPOLL_CTL_ADD..)

注意一个恼人的细节:偶尔可能read()返回0个字节而不意味着文件或套接字在最后。检查是否errno == EAGAIN || errno == EWOULDBLOCK。要检测该情况,然后再次epoll_ctl(..EPOLL_CTL_ADD..)

答案 1 :(得分:0)

当您删除添加的所有内容时,epoll集将为空。据我所知,你无法反省epoll集,以确定是否存在任何文件描述符。因此,由Armin的答案中所述,由您决定epoll集何时变空。

由于你还没有解释你对你的程序的期望,我会猜测你希望它在stdin关闭时退出,因为执行close(0)可能会导致文件描述符0被删除epoll集。但是,列出的代码存在缺陷。如果您继续等待不包含任何文件描述符的epoll集(无论是自动删除还是使用EPOLL_CTL_DEL),epoll_wait将永远等待。

以下代码很好地展示了这一点。

#include <errno.h>
#include <stdio.h>
#include <sys/epoll.h>

int main() {
    int epfd;
    int n;
    struct epoll_event ret;

    epfd = epoll_create(100);

    while((n = epoll_wait(epfd, &ret, 1, -1)) > 0) {
        /* Never gets here. */
        printf("tick!\n");
    }

    return 0;
}

epoll集不包含任何文件描述符,因此epoll_wait永远等待。如果你的程序中碰巧有一个连接到stdin的文件而你的程序中没有其他文件描述符连接到stdin,那么close(0)将从集合中移除fd 0,epoll集变空,下一个epoll_wait永远等待。

通常,您自己管理epoll集中的文件描述符,而不是依赖close调用来自动从集合中删除文件描述符。完成close(0)后,由您来决定是否继续等待epoll集。

我还建议您在<{strong> epoll_wait之后将程序结构更改为read 。这可以保证您在第一次拨打epoll_wait之前获得可能已经到达stdin的任何数据。

另外,要小心这样的代码:

k=0;
while((t=read(0, buf, 100)) > 0) {
    k+=t;
}   
if(k == 0) {
    close(0);
    printf("stdin done\n");
}

如果您假设循环中的read连续返回100后跟0表示某些数据加上文件结尾,则不会调用close(0)。该程序将在epoll_wait上循环并再次等待。最好检查每个read的结果,特别是文件结尾和错误。