zoukankan      html  css  js  c++  java
  • epoll简介(二)

    一:多路复用的举例

            以一个生活中的例子来解释:

            假设你在大学中读书,要等待一个朋友(数据)来访(要读),而这个朋友只知道你在A号楼(socket集合),但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面。

            如果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的。

            现在时代变化了,开始使用多路复用IO模型来处理这个问题。你告诉你的朋友来了A号楼找楼管大妈,让她告诉你该怎么走。这里的楼管大妈扮演的就是多路复用IO的角色(select,poll,epoll)。

     

            select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了。在实际的代码中,select版大妈做的是以下的事情:

    int n = select(&readset,NULL,NULL,100);
    for (int i = 0; n > 0; ++i)
    {
         if (FD_ISSET(fdarray[i], &readset))
        {
            do_something(fdarray[i]);
            --n;
        }
    }
    

            epoll版大妈就比较先进了:她记下了同学甲的信息,比如说他的房间号(socket fd),那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了。epoll版大妈做的事情可以用如下的代码表示:

    n = epoll_wait(epfd,events,20,500);
    for(i=0;i<n;++i)
    {
        do_something(events[n]);
    }
    

            在epoll中,关键的数据结构epoll_event定义如下:

    typedef union epoll_data
    {
        void *ptr;
        int fd;
        __uint32_t u32;
        __uint64_t u64;
    } epoll_data_t;
    
    struct epoll_event 
    {
        __uint32_t events;      /* Epoll events */
        epoll_data_t data;      /* User data variable */
    }; 
    

            可以看到,epoll_data是一个union结构体,它就是epoll版大妈用于保存同学信息的结构体,它可以保存很多类型的信息:fd,指针等等。有了这个结构体,epoll大妈可以不用吹灰之力就可以定位到同学甲。

     

            别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一。再回到那个例子中,如果每到来一个朋友楼管大妈都要全楼的查询同学,那么处理的效率必然就低下了,过不久楼底就有不少的人了。

     

    二:select和poll

            尽管poll和select完成一样的工作,但是poll仍然优于select:

             1:poll无需使用者计算最大的文件描述符值加一和传递该参数。

             2:poll在应对较大值的文件描述符时更具效率。想象一下select监视值为900的文件描述符时的情况,内核需要检查每个集合中的每个比特位,直到第900个。

             3:select的文件描述符集合是静态大小的,所以要做出权衡:要么集合很小,限制了select可以监视的文件描述符的最大值,要么很大,但是效率不高。尤其是当不能确定集合的组成是否稀疏时,对较大掩码的操作效率不高。而用poll则可以创建合适大小的数组。

             4:若用select,文件描述符集合会在返回时重新创建,这样的话,之后每个调用都必须重新初始化它们。poll调用分离了输入(events)和输出(revents),数组无需改变即可重用。

    select的参数类型fd_set没有将文件描述符和事件绑定,它仅仅是一个文件描述符集合。内核对fd_set集合的修改,使应用程序在下次调用select的时候,需要重置3个fd_set集合。

             poll的参数pollfd要比select聪明一些,它把文件描述符和事件都定义在其中,内核每次修改的是pollfd结构体中的revents成员,而events成员保持不变,因此下次调用poll的时候,无需重置。

             5:select的timeout参数在返回时是未定义的。

     

            但是select系统调用也有几个不错的地方:

           1:某些系统不支持poll,所以select的可移植性更好。

           2:select提供了更好的超时方案:直到微妙级。

     

    三:epoll

            select和poll都只能工作在相对低效的LT模式,而epoll可以工作在ET模式,而且还支持EPOLLONESHOT事件,该事件可以进一步减少可读、可写和异常事件触发的次数。

     

            从内核实现原理上来说,select和poll采用的都是轮询的方式,即每次调用都要扫描整个注册文件描述符集合,并将其中就绪的文件描述符返回给用户程序,因此它们检测就绪事件的时间复杂度是O(n)。

            epoll_wait则不同,它采用的是回调的方式。内核检测到就绪的文件描述符时,将触发回调函数,回调函数就将该文件描述符上对应的事件插人内核就绪事件队列。内核最后在适当的时机将该就绪事件队列中的内容拷贝到用户空间。因此epoll_wait无须轮询整个文件描述符集合来检洲哪些事件已经就绪,其算法时问复杂度是0(1)。

     

            但是。当活动连接比较多的时候。epoll_wait的效率未必比就select和poll高,因为此时回调函数被触发得过于频繁。所以epoll_wait适用于连接数量多,但活动连接较少的情况。

     

             从应用程序的角度看,每次select和poll调用,都返回整个用户注册的事件集合,包括就绪的和未就绪的,所以应用程序寻找就绪文件描述符的时间是O(n)。

            epoll采用完全不同的方式来管理用户注册的事件,它在内核中维护事件表,提供独立的系统调用epoll_ctl进行添加,删除,修改事件。这样,每次epoll_wait调用都直接从内核事件表中取得用户注册的事件,而无需反复从用户空间读入这些事件,而且,epoll_wait只返回就绪事件,所以,应用程序寻找就绪文件描述符的时间为O(1)。

     

    四:其他

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

     

            如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。

     

  • 相关阅读:
    [CareerCup] 13.7 Node Pointer 节点指针
    [LeetCode] Bulls and Cows 公母牛游戏
    [CareerCup] 13.6 Virtual Destructor 虚析构函数
    [CareerCup] 13.5 Volatile Keyword 关键字volatile
    [CareerCup] 13.4 Depp Copy and Shallow Copy 深拷贝和浅拷贝
    [CareerCup] 13.3 Virtual Functions 虚函数
    [CareerCup] 13.2 Compare Hash Table and STL Map 比较哈希表和Map
    [CareerCup] 13.1 Print Last K Lines 打印最后K行
    [CareerCup] 12.6 Test an ATM 测试一个自动取款机
    [LeetCode] Serialize and Deserialize Binary Tree 二叉树的序列化和去序列化
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247111.html
Copyright © 2011-2022 走看看