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链表里了

工作流程

  1. 执行epoll_create时,创建了红黑树和就绪list链表。

  2. 执行epoll_ctl时,如果增加fd(socket),则检查在红黑树中是否存在,存在立即返回,不存在则添加到红黑树上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪list链表中插入数据。

  3. 执行epoll_wait时立刻返回准备就绪链表里的数据

要点

  • epoll_event中的epoll_data_t用来存储用户自定义数据

    当与其绑定的fd上发生指定的事件时,epoll_wait返回该event,用户可以拿到该数据

为什么高效(相对selectpoll)?

  • 无需每次调用,都将套接字集合从应用层拷贝到内核层。

    epoll采用红黑树来管理描述符,epoll_ctl执行管理操作

  • 当有IO事件返回时,epoll无需遍历整个描述符集合(O(m),m为IO就绪的数量),而select/poll则是O(n),大大的减少了轮询操作

    epoll返回event的集合(该event结构包含用户数据),应用层只需根据原有数据来分发事件

  • 内核采用slab算法管理epoll相关数据结构,加速了相关操作时间

留言

2016-03-15