1. 介绍

epoll几本上可以理解为异步非阻塞的代名词了,本身也是比较重要的概念。这里我们对其核心的原理做些简单分析理解。

2. poll,select和epoll

epoll也称为reactor、事件驱动、事件轮询。在epoll之前的是poll和select。

2.1 poll/select缺点

  1. 每次调用时要重复地从用户态读入参数。
  2. 每次调用时要重复地扫描文件描述符。
  3. 每次在调用开始时,要把当前进程放入各个文件描述符的等待队列。在调用结束后,又把进程从各个等待队列中删除。

2.2 epoll如何克服poll/select缺点

在实际应用中,select/poll监视的文件描述符可能会非常多,如果每次只是返回一小部分,那么,这种情况下select/poll显得不够高效。

 epoll的设计思路,是把select/poll单个的操作拆分为1个epoll_create+多个epoll_ctrl+一个wait。此外,内核针对epoll操作添加了一个文件系统”eventpollfs”,每一个或者多个要监视的文件描述符都有一个对应的eventpollfs文件系统的inode节点,主要信息保存在eventpoll结构体中。而被监视的文件的重要信息则保存在epitem结构体中。所以他们是一对多的关系。

由于在执行epoll_create和epoll_ctrl时,已经把用户态的信息保存到内核态了所以之后即使反复地调用epoll_wait,也不会重复地拷贝参数,扫描文件描述符,反复地把当前进程放入/放出等待队列。这样就避免了以上的三个缺点。

3.3 epoll实现原理

这个建议看看“蓝形参:在知乎上的回答,写的很好,这里不废话了。epoll 或者 kqueue 的原理是什么?

我这里说下实现非阻塞I/O的几种路线,从性能较差到性能较好。这样揭示了 epoll号在哪里。

  1. 忙轮询:一直询问内核态IO数据有没有准备好,效率很低
  2. 基于选择器的无差别轮询:额外引入一个选择器,告诉程序程序哪些I/O流准备好数据了,程序只在这些流里面无差别轮询。这样显然相比第一种方式减少了轮询的对象数。参考NIO的实现,也是引入了个选择器,只不过应该算是在应用层引入了,而不是系统层面。(个人猜测)
  3. 基于epoll的事件通知: 不需要对所有IO数据流轮询啦~~~那个流准备好数据了,直接内核态通过事件通知告诉你~~皆大欢喜~~~

4. 总结

4.1 关于轮询

poll/select效率不够高是因为对所有IO流采用轮询,这个比较慢。

epoll其实也是轮询,只不过是对产生了IO事件的流进行轮询。此外和基于选择器的差别是——“如何知道IO事件发生是在LINUX内核发生的,利用中断的机制”

4.2 一句话描述epoll本质优点

epoll的本质优点就是利用中断在内核态就确定进程需要对哪些IO事件进行轮询,从而大大提升了性能

这里注意两个关键含义:

  1. 内核态确定需要对哪些IO事件轮询
  2. epoll只对IO事件进行轮询,而不是整个IO流

    参考资料:

    1. 事件触发机制:Poll,Select和Epoll实现原理分析