Linux系列: epoll 高并发的核心
2016-07-14
众所周知, epoll是Linux从Kernel 2.6开始引入的处理I/O多路复用的新方法。其相比以前的select,poll,
在处理大量开放连接的性能上有了非常大的提高。
我甚至觉得只要谈到和网络相关的高并发,在Linux下各种软件的底层都会优先使用epoll。列举几个我知道的:
鼎鼎大名的
nginx,无需介绍;
事件驱动的
Node.js语言;
近几年来越来越火热的高性能
key-value内存数据库redis;
使用
Python开发的全栈式Web框架和异步网络库,tornado;
在Linux下,它们在处理网络I/O事件时都无一例外的使用了epoll。那么epoll到底是什么?它的高并发
能力又是如何实现的?
推荐一篇文章,讲的比较详细:
linux下epoll如何实现高效处理百万句柄的
我们知道, 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之前处理到期事件。
这样,我们就有能力去执行一些在将来某个时间点发生的事件,或者是一些需要周期执行的事件。
下一篇会分析一下tornado的ioloop源码,看一下tornado对上面这个Reactor模式的实现。