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