zoukankan      html  css  js  c++  java
  • select、poll和epoll

    I/O复用:

      在一个进程或者多个进程的需要多个I/O,不能阻塞在一个I/O上而停止不前,而是用到I/O复用。进程预先告知内核需要哪些I/O描述符,内核一旦发现指定的一个或多个I/O条件就绪,则通知进程进行相应操作,这就是I/O复用。

    使用场合:

    1、客户处理多个描述符(交互式输入和网络套接字)

    2、TCP服务器既处理监听套接字,又处理连接套接字

    3、一服务器既处理TCP又处理UDP

    4、一服务器要处理多个服务或多个协议

    select函数:

    允许进程指示内核等待多个事件中的任何一个发生,且只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

    其中:maxfdp1表示探测描述符中的最大值加一(因为描述符从0开始,其表示个数),后面三个参数依次表示读、写和异常描述符集,最后一个表示等待时间。timeout:NULL 永远等待;正数,等待一段时间后返回;0,不等待,检查描述符后立即返回。

    #include <sys/select.h>
    #include <sys/time.h>
    int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);  
                                                  返回:若有就绪描述符则为其数目,若超时为0,出错为-1
    
    struct timeval
    {
       long tv_sec;  //seconds
       long tv_usec;      //microseconds
    };
    
    void FD_ZERO(fd_set *fdset);    //clear all bits in fdset
    void FD_SET(int fd, fd_set *fdset);   //trun on the bit in fdset 
    void FD_CLR(int fd, fd_set *fdset);  //turn off the bit for fd in fdset
    void FD_ISSET(int fd, fd_set *fdset);  //is the bit for fd on in fdset 

    poll函数

      与select函数大致相同,不同在于select描述符最大个数FD_SETSIZE,poll可更大。且传递的结构不同,poll对每个描述符管理起来,select分别用三个数组管理起来。timeout:INFTIM永远等待,0立即返回,正数等待指定毫秒数返回。

    #include <poll.h>
    int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);  //返回:若有就需描述符则为其数目,超时为0,出错为-1
    struct pollfd
    {
      int fd;  //descriptor to check
      short events;  //events of interest on fd
      short revents;  //events that pccurred on fd
    };

     总结:参考http://www.open-open.com/lib/view/open1410403215664.html#articleHeader0

    select缺点:1、单进程可监视文件描述符最大限制1024个,可更改。但select采用轮询方式扫描文件描述符,文件描述符数量越多性能越差(Linux内核中:#define _FD_SETSIZE 1024)

    2、内核、用户空间内存拷贝,select需要赋值大量的句柄数据结构,产生巨大开销;

    3、select返回整个句柄数组,应用程序需要遍历数组查找就绪文件描述符;

    4、select水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将其桃枝进程

    poll:相比select只是数据结构发生变化,用一个结构体数组来表示监视的文件描述符,每一个结构存储监视的文件描述符和其监视事件,并在其中返回监视结果。其监视文件数量没有限制。但是其他缺点和select一样。

    例如:服务器需要支持100万并发连接,在_FD_SETSIZE为1024的情况下,我们至少需要创建1K歌进程才能实现100万的并发连接,除进程间上下文切换的时间开销,从内核、用户空间的内存拷贝,数组轮询等都是系统难以承受和实现的。因此基于select模型的服务器,要达到10万级别的并发访问控制,是很难完成的。

    epoll

      就上面例子中,select/poll都是服务器进程每次都把这100万个连接告诉操作系统(从用户赋值句柄数据结构到内核),让操作系统内核查询这些套接字上是否有事件发生,该过程资源消耗较大,因此select/poll一般只能处理几千的并发连接。

    epoll的设计和实现与select完全不同。epoll通过Linux内核中申请一个建议的文件系统(B+树),吧原先的select/poll分为:

    1、epoll_creat()简历一个epoll对象(epoll文件系统中为这个句柄对象分配资源)

    2、epoll_ctl向epoll对象中添加监视的描述符;

    3、epoll_wait收集发生的事件的连接;

    epoll实现思路:

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

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

       每一个epoll对象都有一个eventpoll结构体,用于存放通过epoll_ctl方法将epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此重复添加的事件也可以通过红黑树而高效的识别出来。

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

    对于每一个事件都会建立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不为空,则把发生的事件赋值到用户态,同时将时间数量返回。

  • 相关阅读:
    [Linux]Vmwaer创建CENTOS7虚拟机[转]
    [游记]二访金陵
    [Android]ADB调试: SecurityException: Injecting to another application requires INJECT_EVENTS permission
    [操作系统]记一次未尽的三星 Galaxy A6s(SM-G6200)刷机过程
    [网络]NAT与内网穿透技术初探【待续】
    结构化系统建模之程序流程图|系统流程图|数据流图
    UML系统建模之用例视图
    [Linux]常用命令之【mkdir/touch/cp/rm/ls/mv】
    [Linux]监控外部用户登录及外部主机连接情况
    [Java SE]Unicode解码
  • 原文地址:https://www.cnblogs.com/weiyi-mgh/p/6885280.html
Copyright © 2011-2022 走看看