0
点赞
收藏
分享

微信扫一扫

epoll源码解析(0) 概述与总结


讨论的内核版本为2.6.38

引言

在开始对源码的学习之前我们先来回顾下epoll对于程序员来说涉及到的主要接口和结构.

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

//以下解释来源于百度,文末附上链接
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

值得一提的是对于epoll_ctl时的event来说,其实默认内核会注册EPOLLHUP,EPOLLHUP与LT.

有了这些函数,对我们来说使用epoll当然也不是什么难事啦,我们需要在使用前使用epoll_create创建一个epfd,然后使用epoll_ctl向epoll结构中添加fd,这一步我们会设置我们需要的事件,如果事件到了,但我们没有注册的话poll触发回调时不会把事件加入到rdllist中,也不会拷贝向用户空间,我们也就自然看不到了.在加入了一些fd以后,我们只需要执行epoll_wait即可,这里有一个小小的问题,就是如果事件在epoll_ctl(ADD)以后,epoll_wait之前来会如何,这时意味着事件已经加入到了rdllist中,我们不必担心,在epoll_wait之前内核会进行检测,如果rdllist不为空,不会进入睡眠,会直接执行,即遍历rdllist,把数据拷贝向用户空间.至于rdllist是什么,后面几篇文章会解释.

我们有必要说说epoll为何如此高效:

  1. 使用内核中的slab机制分配epoll_entry和epitem这些会经常操作的数据结构,slab机制可以把经常使用的对象利用缓存存起来,避免多次初始化,且可以更好的利用硬件的高速缓存.
  2. 在我看的这个版本的内核代码实现中(2.6.38),所有在一个文件系统内创建的epoll共享一个inode,而使用不同的fd,这样更好的节省了内存.
  3. 一个巧妙的回调的使用是epoll如此高效的最主要的原因,相比与select和poll,epoll在注册的fd中注册一个回调,fd会在poll的时候触发回调,把fd相对应的epitem结构加入rdllist中,这样每次epoll_wait被触发后发送往用户态的fd就都是我们注册的event发生的fd了,避免了用户态的遍历,这样可以在有大量不活跃fd时有可观的效率提升.会在epoll_ctl篇中提及.
  4. 因为我们是注册了一个回调,所以我们也不必在每次epoll_wait后重新注册事件,这也是一个重要的点.
  5. 利用红黑树存储epitem,使得增删查的效率有了显著的提升.

我建议的学习路线是先看简单的浏览一遍基本数据结构,即epitem和eventpoll,而不必深究其作用,作用等到看代码时再理解,这样可以有一个更深的印象,也不会在开始时一脸懵.

下面是文章的正文部分:
​epoll源码解析(1) epoll_create​​​​

epoll源码解析(2) epoll_ctl​​​​

epoll源码解析(3) epoll_wait

参考:

​​http://edsionte.com/techblog/archives/4019 slab​​

​poll机制

SYSCALL_DEFINEx


举报

相关推荐

0 条评论