多路复用

为什么会考虑到深入理解多路复用?在Http/2,Redis等内容中,反复提到多路复用带来的效率提升,也只有了解了基础概念,才能掌握它们,一步一步来吧。

了解多路复用前,先对五中IO模型进行初步了解。
省略--后续补充

多路复用最重要的知识点是因为内部用了一个红黑树记录添加的socket,用了一个双向链表接收内核触发的事件(双向链接描述错误,“双向链表的每个节点都是基于epitem结构中的rdllink成员”)

 

多路复用
红黑树

 

就是因为多了这个存储,可以直接拿到就绪socket,而不用像select那样一个个检查。由于epoll需要往这个结构里添加或删除数据,就要求这个结构能够快速的添加和删减元素,双向链表刚好比较适合。
情况分类|添加|删除

  • 双向链表 时间复杂度
情况分类 添加 删除
最好头节点 O(1) O(1)
最好尾节点 O(1) O(1)
平均 O(n) O(n)

细节

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。eventpoll结构体如下所示:

 

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

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

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

在epoll中,对于每一个事件,都会建立一个epitem结构体,如下所示:

struct epitem{
    struct rb_node  rbn;//红黑树节点
    struct list_head    rdllink;//双向链表节点
    struct epoll_filefd  ffd;  //事件句柄信息
    struct eventpoll *ep;    //指向其所属的eventpoll对象
    struct epoll_event event; //期待发生的事件类型
}

当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了。

上一篇:df、du、fdisk、lsblk区别


下一篇:VGG论文学习心得