我正在学习使用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) 谢谢!
答案 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
的结果,特别是文件结尾和错误。