研究Node的时候,对于其采用事件驱动模型很是好奇,能轻松解决高并发的问题,Nginx的IO多路复用模型也是采用事件驱动的。

这里不得不提一下select和poll

Select和Poll IO多路复用

(1)Select

当有I/O事件发生,Select轮询所有流,找到可以读出或者写入的流,所以Select的事件复杂度是O(n),所以同步处理的N值很大的时候,处理时间就越长、性能越差。

缺点:

  1. 单个进程能监视的fd数量有最大限制,linux定义是1024,可以自己重新定义,不过这样一来,N值就更大了,性能进一步被拖低,没啥意义。
  2. 需要维护一个存放大量fd的数据结构,在用户空间与内核空间传递该数据结构时复制的开销大。
  3. Select需要轮询所有fd才能知道哪些句柄发生事件。
  4. Select的事件触发方式时水平触发,即程序如果没有对一个已经就绪的fd进行IO读写,那么每次Select调用,还是会将这些就绪的fd通知进程。

(2)Poll

本质上没有与Select有太大区别,除了Poll是使用链表保存文件描述符,所以无需担心单一进程监视文件数量的限制。但是,Select中的2、3、4问题依然存在

所以由于这几个缺点导致以上两种处理模型很难处理成上万的并发量。

Epoll

Epoll 是在Linux2.6内核中才引入的,Epoll可以理解为 event poll。与上面两种模型轮询不一样,当有I/O发生,流发生什么样的I/O事件会进入到rdlist双链表中,当Epoll检查是否有事件发生,只需要查看rdlist是否为空就行了,如果不为空,则将发生的事件复制到用户态,将事件数量返回给用户。

那么我们来讲一下Epoll的机制的实现流程,分为以下3部分:

(1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)

(2)调用epoll_ctl向epoll对象中对连接的套接字操作

(3)调用epoll_wait收集发生的事件的连接

 

那么进程启动的时候会调用(1)建立一个epoll对象,然后在需要的时候向这个epoll对象中添加或者删除连接(2),检查(3)是否有事件发生,有的话,将发生的事件复制到用户态。

Linux内核中Epoll机制的实现思路

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员,与epoll的使用方式密切相关。一个是rbr,红黑树的根节点,另外一个就是刚才提到的rdlist,通过epoll_wait返回给用户的事件双向链表。eventpoll结构体如下所示:

struct eventpoll{
    ....
    /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/
    struct rb_root  rbr;
    /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/
    struct list_head rdlist;
    ....
};

每个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树节点中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是O(lgn))。

而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到eventpoll的rdlist双链表中。

以下两张图可以很清楚地知道eventpoll 红黑树的数据结构。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

我们再来复习一下整个事件驱动流程是怎样的

  1. epoll_create创建一个epoll对象对应eventpoll根节点
  2. epoll_ctl增删改事件,即epitem结构。
  3. epoll_wait收集在epoll监控中发生的事件。

 

参考资料:

https://blog.csdn.net/shenya1314/article/details/73691088

https://www.cnblogs.com/aspirant/p/9166944.html

https://www.cnblogs.com/apprentice89/p/3234677.html

您或许感兴趣

[2018-09-17]Nginx的负载均衡及配置
[2018-09-17]正向代理以及反向代理
[2018-10-14]Simple Rtmp Server (srs) 与 Flv.js 实现直播功能

发表评论

电子邮件地址不会被公开。