epoll
API
1 | int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); |
返回值
0 返回IO就绪的文件描述符总数;
==0 超时返回
<0(-1) 出错(需要注意,该调用为慢速系统调用,当被信号中断时,返回EINTR)
原理
调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的fd外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。而且,通常情况下即使我们要监控百万计的fd,大多一次也只返回很少量的准备就绪fd而已,所以,epoll_wait仅需要从内核态copy少量的fd到用户态而已。那么,这个准备就绪list链表是怎么维护的呢?当我们执行epoll_ctl时,除了把fd放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个fd的中断到了,就把它放到准备就绪list链表里。所以,当一个fd(例如socket)上有数据到了,内核在把设备(例如网卡)上的数据copy到内核中后就来把fd(socket)插入到准备就绪list链表里了
工作流程
执行
epoll_create
时,创建了红黑树和就绪list链表。执行
epoll_ctl
时,如果增加fd(socket),则检查在红黑树中是否存在,存在立即返回,不存在则添加到红黑树上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪list链表中插入数据。执行
epoll_wait
时立刻返回准备就绪链表里的数据
要点
epoll_event
中的epoll_data_t
用来存储用户自定义数据当与其绑定的
fd
上发生指定的事件时,epoll_wait
返回该event
,用户可以拿到该数据
为什么高效(相对select
,poll
)?
无需每次调用,都将套接字集合从应用层拷贝到内核层。
epoll采用红黑树来管理描述符,
epoll_ctl
执行管理操作当有IO事件返回时,epoll无需遍历整个描述符集合(O(m),m为IO就绪的数量),而select/poll则是O(n),大大的减少了轮询操作
epoll返回event的集合(该event结构包含用户数据),应用层只需根据原有数据来分发事件
内核采用slab算法管理epoll相关数据结构,加速了相关操作时间