epoll-I / O事件通知工具。使用前需引用 头文件#include <sys / epoll.h>
epoll API执行与poll类似的任务:监视多个文件描述符,以查看是否可以在任何文件描述符上进行I / O。 epoll API可以用作边缘触发或级别触发的接口,并且可以很好地扩展到大量监视的文件描述符。提供了以下系统调用来创建和管理epoll实例:
①epoll_create创建一个epoll实例并返回引用该实例的文件描述符。
②然后通过epoll_ctl注册对特定文件描述符的兴趣。当前在epoll实例上注册的文件描述符集有时称为epoll集。
③epoll_wait等待I / O事件,如果当前没有可用的事件,则阻塞调用线程。
侦听器是一个非阻塞套接字,已在其上调用listen。
建议用法示例:
尽管将epoll用作级别触发接口时确实具有与poll相同的语义,但是边缘触发的用法需要更多说明,以避免应用程序事件循环中的停顿。 在此示例中,侦听器是一个非阻塞套接字,已在其上调用listen。 函数do_use_fd使用新的就绪文件描述符,直到EAGAIN由read或write返回。 事件驱动的状态机应用程序应在收到EAGAIN后记录其当前状态,以便在下一次对do_use_fd的调用时,它将继续从其先前停止的位置读取或写入。
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}
当用作边缘触发接口时,出于性能原因,可以通过指定(EPOLLIN | EPOLLOUT)在epoll接口(EPOLL_CTL_ADD)中添加一次文件描述符。 这样可以避免在EPOLLIN和EPOLLOUT之间连续切换,并使用EPOLL_CTL_MOD调用epoll_ctl。
问题和解答:
Q0:用于区分epoll集中注册的文件描述符的密钥是什么?
A0:密钥是文件描述符号和打开文件描述(也称为“打开文件句柄”,即打开文件的内核的内部表示)的组合。
Q1:如果在epoll实例上两次注册相同的文件描述符,会发生什么情况?
A1:您可能会得到EEXIST。但是,可以将重复的(dup,dup2,fcntl F_DUPFD)描述符添加到同一epoll实例。如果重复的文件描述符使用不同的事件掩码注册,则这对于筛选事件可能是一种有用的技术。
Q2:两个epoll实例可以等待相同的文件描述符吗?如果是,事件是否同时报告给两个epoll文件描述符?
A2:是的,事件将报告给这两个事件。但是,可能需要仔细编程才能正确执行此操作。
Q3:epoll文件描述符本身是否可以 poll/epoll/selectable?
A3:是的。如果epoll文件描述符有等待事件,则它将指示为可读。
Q4:如果尝试将epoll文件描述符放入其自己的文件描述符集中会发生什么?
A4:epoll_ctl调用将失败(EINVAL)。但是,您可以在另一个epoll文件描述符集中添加一个epoll文件描述符。
Q5:我可以通过UNIX域套接字将epoll文件描述符发送到另一个进程吗?
A5:是的,但是这样做没有意义,因为接收过程在epoll集中将没有文件描述符的副本。
Q6:关闭文件描述符会导致它自动从所有epoll集中删除吗?
A6:是,但是请注意以下几点。文件描述符是对打开文件描述的引用(请参见open(2))。每当描述符是通过dup,dup2,fcntl F_DUPFD或fork复制,将创建一个引用相同打开文件描述的新文件描述符。开放文件描述将继续存在,直到所有引用它的文件描述符都已关闭。仅在关闭所有引用基础打开文件描述的文件描述符之后(或者如果使用epoll_ctl(2)EPOLL_CTL_DEL明确删除了描述符之后),才从epoll集中删除文件描述符。这意味着即使关闭了作为epoll集一部分的文件描述符,如果其他引用相同基础文件描述的文件描述符保持打开状态,则可能会报告该文件描述符的事件。
Q7:如果在epoll_wait(2)调用之间发生多个事件,它们是合并还是分开报告?
A7:他们将被合并。
Q8:对文件描述符的操作是否会影响已收集但尚未报告的事件?
A8:您可以对现有文件描述符执行两项操作。在这种情况下,删除将毫无意义。修改将重新读取可用的I / O。
Q9:使用EPOLLET标志(边沿触发的行为)时,我是否需要连续读写文件描述符直到EAGAIN?
A9:从epoll_wait(2)接收事件应向您建议该文件描述符已准备就绪,可以执行请求的I / O操作。您必须考虑到它准备就绪,直到下一次(非阻塞)读/写产生EAGAIN为止。何时以及如何使用文件描述符完全取决于您。对于面向数据包/令牌的文件(例如,数据报套接字,标准模式下的终端),检测读/写I / O空间结束的唯一方法是继续读/写直到EAGAIN。对于面向流的文件(例如管道,FIFO,流套接字),还可以通过检查从目标文件描述符读取/写入到目标文件描述符的数据量来检测读取/写入I / O空间已用尽的情况。例如,如果通过要求读取一定数量的数据来调用read(2),而read(2)返回的字节数较少,则可以确定已经用完了文件描述符的读取I / O空间。使用write(2)进行写入时也是如此。 (如果不能保证受监视的文件描述符始终引用面向流的文件,请避免使用后一种技术。)