4

I'm learning to use epoll, and I wrote the following example

#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;
}

If you try running it in the terminal it won't work properly since fds 0, 1 and 2 all point to same open file, so close(0) won't remove stdin from the epoll set. You can get around this by doing "cat | ./a.out". Dirty trick, I know, but setting up a small example with named pipes or sockets would be more complicated.

Now, everything works and the file is removed from the epoll set, but then the next epoll_wait call blocks permanently since it's on an empty set! So I would need to detect if the epoll file descriptor (epfd) is an empty epoll set.

How can I get around this? (in a general manner, not just calling exit when stdin is done) Thanks!

4

2 回答 2

0

基本上,如果您“正确”地使用 epoll,那么您永远不会遇到意外空 epoll 集的情况。你应该知道什么时候有更多的事情要做。好吧,或者至少是这样的理论。让我回顾一下:

您在这里使用 EPOLLET(恕我直言,一般来说是正确的想法)。这意味着文件描述符 0 在返回时从 epoll 中删除&ret。此时,您应该像您一样通过从 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..)一次。

于 2013-04-18T13:34:16.517 回答
0

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

由于您没有解释您对程序的期望,我猜测您希望它在标准输入关闭时退出,因为执行 aclose(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永远等待。如果您的程序中碰巧有一个文件连接到标准输入,而您的程序中没有其他文件描述符连接到标准输入,close(0)则将从集合中删除 fd 0,epoll 集合变为空,并且下一个将epoll_wait永远等待。

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

我还建议您将程序的结构epoll_wait 更改read. 这可以保证您在第一次调用epoll_wait.

另外,请注意以下代码:

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专门针对文件结尾和错误的结果。

于 2013-04-26T07:05:12.297 回答