epoll 笔记
epoll
epoll 是LINUX 特有的I/O 复用函数。它在使用和实现上与select、poll 有很大的差异。 epoll 使用一组函数来完成任务,而不是单个函数。
epoll 把用户关心的文件描述符上的时间放在内核里的一个事件表(红黑树)中,而无需像select和poll 那样每次调用都要重复传入文件描述符集或者事件集。
但是epoll需要一个额外的文件描述符,来唯一表示内核中的这个事件表,这个文件描述符使用epoll_create函数来创建:
int epoll_create(int size), 该函数返回文件描述符将用作其他所有epoll 系统调用的第一个参数,以指定要访问的内核事件表。
int epoll_ctl (int epfd, int op, int fd, struct epoll_even* event)
fd 参数是要操作的文件描述符,op 参数则指定操作类型,操作类型有如下三种:
- EPOLL_CTL_ADD 往事件表中注册fd 上的事件
- EPOLL_CTL_MOD 修改 fd 上的注册事件
- EPOLL_CTL_DEL 删除fd 上注册事件
event 参数指定事件,他是epoll_event 结构指针类型,
epoll_event 的定义如下:
struct epoll_event {
__unit32_t events; // epoll 事件
epoll_data_t data; // 用户数据
}
其中event成员描述事件类型,epoll 支持的时间类型和poll 基本相同, 表示epoll 支持的事件类型与poll 基本相同,表示epoll 事件类型的宏是在poll 对应的宏前加上“E”。
epoll_data_t 的定义如下:
typedef union epoll_data {
void* ptrl;
int fd;
unit32_t u32;
unit64_t u64;
} epoll_data_t;
epoll_data_t 是一个联合体其4个成员中使用最多的是fd, 它指定事件所从属的目标文件描述符。
epoll系列系统调用的主要接口是epoll_wait 函数,它在一段超时事件内等待一组文件描述符上的事件:
#include<sys/epoll.h>
int epoll_wait (int epfd, struct epoll_event* events, int maxevents, int timeout)
该函数成功时,返回就绪的文件描述符的个数,失败时返回-1并设置 errno。epoll_wait 函数如果检测到事件,就将所有就绪的事件从内核事件表中复制到他的第二个参数events 指向的数组中。这个数组值用于输出epoll_wait检测到的就绪事件,不像select 和 poll 数组参数那样,既用于传入用户注册事件,也用于输出内核检测到的就绪事件。这样就极大的提高了程序索引就绪文件描述符的效率。
int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
for(int i = 0; i < ret; i++){
int sockfd = events[i].data.fd;
/* sockfd 肯定就绪,直接处理 */
}
epoll 对文件描述符的操作有两种模式:LT(电平触发) 模式和 ET(边沿触发) 模式,采用了LT工作模式的文件描述符, 当成熟检测到事件发生,并将此事件同质应用程序之后,应用程序可以不立即处理该事件,这样,当应用程序下一次调用epoll_wait 时候,还会再次向应用程序同质次事件,知道事件被处理。而采用ET 工作模式的文件描述符,epoll_wait 检测到其上有事件发生,并将此事件通知应用程序后,应用程序会立即处理该事件,因为后续的epoll_wait 调用将不再向应用程序通知这一时间。可见ET模式在很大程度上降低了同一个epoll 时间被重复触发的次数,因此效率较高。