我正在学习使用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)
谢谢!

最佳答案

基本上,如果您“正确”地使用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..)

09-20 07:19