tylderen +

Linux系列: epoll 高并发的核心

众所周知, epollLinuxKernel 2.6开始引入的处理I/O多路复用的新方法。其相比以前的selectpoll, 在处理大量开放连接的性能上有了非常大的提高。

我甚至觉得只要谈到和网络相关的高并发,在Linux下各种软件的底层都会优先使用epoll。列举几个我知道的:

鼎鼎大名的 nginx,无需介绍;

事件驱动的Node.js语言;

近几年来越来越火热的高性能key-value内存数据库redis

使用Python开发的全栈式Web框架和异步网络库, tornado

Linux下,它们在处理网络I/O事件时都无一例外的使用了epoll。那么epoll到底是什么?它的高并发 能力又是如何实现的?

推荐一篇文章,讲的比较详细: linuxepoll如何实现高效处理百万句柄的

我们知道, 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);  

epoll_create是用来创建一个 epoll对象,完成一些需要的数据结构的建立;

epoll_ctl用来注册socket等到epoll,或者从epoll中移除正在被监控的socket等;

epoll_wait将注册在epoll的准备就绪的句柄返回然后就可以进行处理。

大致了解了epoll,再来说说处理并发I/O比较常见的一种模式,Reactor模式。 中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上; 一旦有I/O事件到来或是准备就绪,多路复用器返回并将相应I/O事件分发到对应的处理器中。

所以,从代码层面,Reactor模式的核心,是一个大的无限循环,在这个循环里,调用epoll_ctl, 将socket等注册到epoll,再调用epoll_wait,等待epoll_wait返回,然后遍历epoll_wait返回 的结果,并用对应的epoll_ctl注册的回调函数进行处理,最后回到循环的开始, 周而复始。

一些基于Reactor模式的软件或库不光能处理I/O事件,还在事件循环里加入了对定时事件的处理。

定时事件的处理,很巧妙的利用了epoll_wait的一个参数timeout,每次调用epoll_wait时, 其参数timeout就等于下次定时事件的到期时间,epoll_wait如果在timeout内有I/O事件 返回,就继续处理这些事件,如果等到timeout没有I/O事件,那么也会因为timeout到期而 返回,此时也表示有定期事件到期了,就会在下一次调用epoll_wait之前处理到期事件。

这样,我们就有能力去执行一些在将来某个时间点发生的事件,或者是一些需要周期执行的事件。

下一篇会分析一下tornadoioloop源码,看一下tornado对上面这个Reactor模式的实现。

Blog

Opinion

Project