zoukankan      html  css  js  c++  java
  • IO复用的三种方法(select,poll,epoll)深入理解

    (一)IO复用是Linux中的IO模型之一,IO复用就是进程告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程处理,从而不会在单个IO上阻塞了,Linux中,提供了select、poll、epoll三种接口来实现IO复用
    (二)select:
    缺点:

        单个进程能够监视的文件描述符的数量存在最大限制,通常是1024。由于select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;
        内核/用户空间内存拷贝,select需要大量的句柄数据结构,产生巨大开销;
        select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生事件;
        select的触发方式为水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么下次select调用还会将这些文件描述符通知进程;

    (三)poll:

        使用链表保存文件描述符,没有了文件描述符的限制,但其他的三个缺点依然存在

    (四)epoll:
    上面所说的select缺点都不存在,epoll使用了一个文件描述符管理了多个文件描述符。拿select模型为例,假设我们服务器需要支持100万个并发链接,则在_FD_SETSIZE为1024的情况下,则我们至少需要开辟1K个进程才能实现100万的并发连接,除了进程上下文切换的时间消耗,从内核到用户空间的拷贝,数据轮询,是系统难以承受的,因此,基于select模型的服务器程序,要达到10万级别的并发访问,是一个很难完成的任务。
    epoll的设计与select完全不同,epoll通过在Linux内核申请一个简易的文件系统(文件一般使用什么数据结构实现?B+树),把原先的select/epoll调用分为3个部分:

        调用epoll_create()建立了一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
        调用epoll_ctl()向epoll对象添加这100万个连接的套接字(ip地址+端口号)
        调用epoll_wait()收集发生事件的连接

    epoll机制实现思路:
    当某一进程调用epoll_create()方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用密切相关

    struct eventpoll
    {
        //............
        //红黑树的根节点,这棵树的每个节点代表着用户告诉内核需要监控的文件描述符
        struct rb_root rbr;
        //调用epoll_wait()函数后满足条件的文件描述符会被添加到rdlist中,也是后期返回用户的事件
        struct list_head rdlist;
    };

    每一个epoll对象都有一个独立的eventpoll结构体,用于存放epoll_ctl()方法向epoll对象中添加进来的事件,这些事件都会存放在这颗红黑树中,红黑树的增删查改的效率为logn,其中n为数的高度。
    而所有添加到epoll对象中的事件都会与设备(网卡)驱动程序建立回调关系,当相应的事件发生时会调用这个回调方法,这个回调方法在内核中叫做ep_poll_back,它会将发生的事件添加到rdlist双链表中。
    在一个epoll对象中,对于每一个事件,都会建立一个epitem结构体如下:

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


    当调用epoll_wait()检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可,如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户。
    从上面的讲解可知:通过红黑树和双链表数据结构并结合回调机制,才有如此高效的epoll
    epoll在使用ET模式的时候一定是非阻塞的,如果是阻塞的话,一个任务监控多个文件描述符时,当一个描述符阻塞就会使其他的描述符所在的任务失败(饿死)
    ————————————————
    版权声明:本文为CSDN博主「_kean」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/C1029323236/java/article/details/90551959

  • 相关阅读:
    mysql InnoDB 索引小记
    Linux shell 脚本小记
    Java中Integer的源码学习
    nginx小记
    位运算小结
    Redis小结
    CSS中nth-child和nth-of-type的简单使用
    Linux awk小记
    mysql小记--基础知识
    让44.1版本的sketch打开更高版本的sketch文件
  • 原文地址:https://www.cnblogs.com/cnhk19/p/13215264.html
Copyright © 2011-2022 走看看