1. epoll
的优越性
上一节介绍的select
有几个缺点:
- 存在最多监听的描述符上限
FD_SETSIZE
- 每次被唤醒时必须遍历才能知道是哪个描述符上状态
ready
,CPU随描述符数量线性增长 - 描述符集需要从内核copy到用户态
这几个缺点反过来正是epoll
的优点,或者说epoll
就是为了解决这些问题诞生的:
- 没有最多监听的描述符上限
FD_SETSIZE
,只受最多文件描述符的限制,在系统中可以使用ulimit -n
设置,运维一般会将其设置成20万以上 - 每次被唤醒时返回的是所有
ready
的描述符,同时还带有ready
的类型 - 内核态与用户态共享内存,不需要copy
2. 简述epoll
的工作过程
2.1 创建
首先由epoll_create
创建epoll
的实例,返回一个用来标识此实例的文件描述符。
2.2 控制
通过epoll_ctl
注册感兴趣的文件描述符,这些文件描述符的集合也被称为epoll set
。
2.3 阻塞
最后调用epoll_wait
阻塞等待内核通知。
3. 水平触发(LB)和边缘触发(EB)
epoll
的内核通知机制有水平触发和边缘触发两种表现形式,我们在下面例子中看一下两者的区别。
有一个代表读的文件描述符(
rfd
)注册在epoll
上在管道的写端,写者写入了2KB数据
调用
epoll_wait
会返回rfd
作为ready的文件描述符管道读端从
rfd
读取了1KB数据再次调用
epoll_wait
如果rfd
文件描述符以ET的方式加入epoll
的描述符集,那么上边最后一步就会继续阻塞,尽管rfd
上还有剩余的数据没有读完。相反LT模式下,文件描述符上数据没有读完就会一直通知下去。
4. epoll
的两个数据结构
4.1 epoll_event
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};