唯快不破:epoll的原理和用法

来源:互联网 发布:mysql front新建数据库 编辑:程序博客网 时间:2024/05/22 10:35

设想一个场景:有100万用户同时与一个进程保持着TCP连接,而每一个时刻只有几十个或几百个TCP连接时活跃的(接收到TCP包),也就是说,在每一时刻,进程值需要处理这100万连接中的一小部分连接。那么,如何才能高效地处理这种场景呢?进程是否在每次询问操作系统收集有事件发生的TCP连接时,把这100万个连接告诉操作系统,然后由操作系统找出其中有事件发生的几百个连接呢?实际上,在Linux内核2.4版本以前,那时的select或者poll事件驱动方式就是这样做的。

       这里有一个分厂明显的问题,即在某一时刻,进程收集有事件的连接时,其实这100万连接中的大部分都是没有事件发生的。因此,如果每次收集事件时,都把这100万连接的套接字传给操作系统(这首先就是用户态内存到内核态内存的大量复制),而由操作系统内核寻找这些连接上有没有未处理的事件,将会是巨大的资源浪费,然而select和poll就是这样做的,因此他们最多只能处理几千个并发连接。而epoll不这样做,他在linux内核中申请了一个简易的文件系统,把原先的一个select或者poll调用分成了3个部分:调用epoll_create建立1个epoll对象(在epoll文件系统中给这个句柄分配资源)、调用epoll_ctl向epoll对象中添加这100万个连接的套接字、调用epoll_wati收集发生事件的连接。这样,只需要在进程启动时建立1个epoll对象,并在需要的时候向它添加或删除连接就可以了,因此,在实际收集事件时,epoll_wait的效率就会非常高,因为调用epoll_wait时并没有向它传递着100万个连接,内核也不需要去遍历全部的连接。

      介绍epoll是怎么处理这种情况的

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

     struct   eventpoll{

     /*红黑树的跟节点,这棵树中存储着所有添加到epoll中的事件,也就是这个epoll监控的事件*/

      struct rb_root_rbr;

     //双向链表tdllist保存着将要通过epoll_wait放回给用户的、满足条件的事件

     struct  list_head_rdllist;


}

每一个epoll对象都有一个独立的eventpoll结构体,这个结构体会在内核空间中创造独立的内存,用于存储使用epoll_ctl方法想epoll对象中添加进来的事件。这些事件都会挂到rbr红黑树中,这样,重复添加的事件就可以通过红黑树而高效标示出来(epoll_ctl方法会很快)。

     所有添加到epoll中的事件都会与设备(如网卡)驱动程序建立回调关系,也就是说,相应的事件发生时会调用这里的回调方法。这个回调方法在内核中叫做ep_epoll_callback,它会把这样的事件放到上面的rdllist双向链表中。在epoll中,对于每一个事件都会建立一个epitem结构体。这里包含每一个事件对应着的信息。

      当调用epoll_wait检查是否有发生事件的连接时,只是检查eventpoll对象中的rdllist双向链表是否有epitem元素而已,如果rdllist链表不为空,则把这里的事件复制到用户态内存中,同时将时间数量返回给用户,因此,epoll_wait的效率非常高,epoll_ctl在向epoll对象中添加、修改。删除事件时,从rbr红黑树中查找事件也非常快,也就是说,epoll是非常高效的,它可以轻易地处理百万级的并发连接。













http://blog.csdn.net/yusiguyuan/article/details/24833339

原创粉丝点击