@FunC
2018-05-15T19:27:20.000000Z
字数 1531
阅读 1916
unix
开销昂贵;即使是多线程,线程间的通信工作仍会让编程工作变得复杂
当然多线程也有用武之地,正如之前在 Node 的笔记中提到的,面对计算密集型(CPU-bound)任务还是用多线程处理更佳。即 event loop + thread poll
因为select() 和 poll() 需要检查所有关心的文件描述符的就绪状态,因此当需要检查大量的文件描述符时,其性能将大幅下降。归根到底在于程序重复调用这些借口,而内核不会在每次成功调用后记录下他们,导致了大量的重复工作。
epoll() 和信号驱动I/O 能解决这个问题。
epoll API 由以下3个系统调用组成:
* epoll_create() 创建一个epoll实例,返回代表该实例的文件描述符
* epoll_ctl() 用于操作同 epoll 实例相关联的兴趣列表(增删改)。
* epoll_wait() 返回与 epoll 实例相关联的就绪列表中的成员(关键)
下面稍微介绍一下这三个 API。
#include <sys/epoll.h>
int epoll_create(int size);
/* 返回:成功则返回相应的文件描述符,出错为 -1 */
返回值代表新创建的 epoll 实例的文件描述符,在后面两个 epoll 系统调用中表示对应的 epoll 实例。当这个文件描述符不再需要时,应该通过 close() 来关闭。
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
/* 返回:成功时为 0,出错为 -1 */
其中参数 op 指定要执行的操作(增删改)
参数 ev 指向 结构体 epoll_event 的指针,定义如下:
struct epoll_event {
unit32_t event;
epoll_data_t data;
};
类似于 poll 中的参数,包含关联的描述符以及发生的事件
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);
/* 返回:就绪的描述符数量,超时返回 0,出错返回 -1 */
其中参数 evilest 指向的结构体数组中返回有关就绪态文件描述符的信息,其空间由调用者通过参数 maxevents 申请。
默认情况下,epoll 提供的是水平触发通知(即检查文件描述符当前是否可以非阻塞执行I/O操作)。epoll 同时也支持边缘触发(即文件描述符自上次状态检查以来有了新的 I/O活动)
要使用边缘触发通知,要在调用 epoll_ctl() 时在 ev.evnts 字段中指定 EPOLLET 标志:
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN | EPOLLET;
if (epoll+ctl(epfd, EPOLL_CTL_ADD, fd, ev) == -1)
errExit("epoll_ctl");
优点:
* 当检查大量的文件描述符时,epoll 的性能延伸性比 select() 和 poll() 高很多
* epoll API 既支持水平触发,也支持边缘触发
与信号驱动I/O相比:
* 可以避免复杂的信号处理流程(如信号队列溢出时的处理)
* 灵活性高,可以指定我们希望检查的事件类型。
缺点:
* epoll API 为 Linux 系统专有,可移植性一般(如 MacOS 使用kqueue)