烦恼一般都是想太多了。

0%

epoll系统调用

在进行多路复用的时候 select 最为人诟病的就是,每次有事件发生,其都必须要遍历其观察的文件描述符集,才能知道到底是哪里发生了事件。如果我们要关注一万个文件描述符,然后是第 9999 个发生了事件,那么他必须遍历到最后,这是非常可怕的事情。因此才有了 poll 的诞生。

描述

#include <sys/epoll.h>

即可使用相关的函数了。

epoll API 进行和 poll 差不多的工作:监控多个文件描述符上面的 I/O 事件。epoll API 可以用来作为一个水平触发或者边缘触发的接口,同时对于数量巨大的文件描述符也会工作得很好。下面的几个系统调用用来建立和管理 epoll 实例:

  • epoll_create() 会创建一个 epoll 实例,并返回一个引用这个实例的文件描述符。(epoll_create1()epoll_create() 进行了扩展)。
  • epoll_ctl() 用来注册我们感兴趣的文件描述符。我们注册到 epoll 实例的 文件描述符集有时也会被叫做 epoll 集合。
  • epoll_wait() 等待 I/O 事件,如果当前没有事件的话就会阻塞调用线程。

水平触发(LT)与边缘触发(ET)

两者的不同如下。假设发生了这样的场景:

  1. 代表管道读端的文件描述符 rfd 注册到 epoll 实例。
  2. 管道的写端写入了 2KB 数据。
  3. 调用 epoll_wait() 返回 rfd 表示此文件描述符上已有准备好的事件。
  4. 从管道读端读了 1KB 。
  5. 我们再次调用 epoll_wait()

如果 rfd 是以 EPOLLET 标志注册到 epoll 实例的,上面的第 5 步会挂起,尽管还有数据可读。这是因为边缘触发(ET) 只会在被监控的文件描述符上有变化的时候才会传递事件。所以在上述的操作中才会出现阻塞情况。

因此在以 ET 形式进行使用的时候,文件描述符要设置为非阻塞的。建议的使用方式是:

  1. 使用非阻塞文件描述符
  2. 同时 只在 read(), write() 返回 EAGAIN 后再进行等待事件。

当我们不指定 EPOLLET 时 (这是默认情况),是当做水平触发来使用的, epoll() 就是一个 快速版本的 poll()

例子

一个典型的用法。

#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);
}
}
}