如何检测空的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; } 

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

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

我怎样才能解决这个问题? (一般来说,不要只是在stdin完成时调用exit)谢谢!

基本上,如果您正确使用epoll,那么您绝不应该有一个意外空的epoll集。 你应该知道什么时候有更多的事情要做。 那么,或者至少这是理论。 让我回顾一下:

你在这里使用EPOLLET(这就是imho,一般认为)。 这意味着文件描述符0在&ret返回时将从epoll中移除。 在这一点上,你应该像读你一样从0中读取一些数据来处理它,然后通过再次向文件描述符0添加文件描述符(除非它是关闭的),然后“重新设置”它。 对于这个应该如何工作的例子,删除内部循环,只是:

 k = read(0, buf, 100); 

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

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

当你删除了所有添加的内容时,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永远等待。 如果在程序中碰巧有一个文件连接到标准输入,并且程序中没有其他文件描述符连接到标准输入, close(0)将从设置中删除fd 0,epoll集变为空,下一个epoll_wait永远等待。

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

我还建议您在read 之后将程序的结构更改为epoll_wait 。 这可以保证在第一次调用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的结果,特别是文件结束和错误。