抛砖引玉篇--IO复用技术(select、poll、epoll)

/ --*-- 2018.7.18 --*-- /

 

%. select、poll、epoll这三组io复用系统调用,

   都能够同时监听多个文件描述符的事件。

   它们等待由timeout指定的超时时间,

   直到有一个或多个事件发生时返回,

   返回值为已经就绪的fd的数量,

   返回0表示没有事件发生。

 

% 下面从 事件集、最大支持的fd数量、工作模式、具体实现进行比较:

 

  % 事件集:这三个调用通过某种结构体变量通知内核监听哪些fd上的哪些事件,并通过该结构体类型的参数获取内核处理的结果。

% select:

  使用fd_set结构体,该结构体并没有将event和fd绑定,

  所以select使用三个fd_set参数分别表示读、写、异常事件。

  用户通过这三个fd_set参数通知内核监听哪些事件,

  内核通过修改这三个fd_set返回处理结果。

  这将导致:

  @1.select能处理的事件类型有限。

  @2.每次调用select时,都必须重置fd_set。

 

% poll:

  使用pollfd结构体,该结构体将events和fd绑定在一起。

  该结构体输入用events,返回用revents。

  相比select,有以下优势:

  @1.events和fd绑定,可以用一个pollfd类型参数表示多种类型事件,

     所以减少参数个数,简化编程接口。

  @2.每次调用poll时,不必重置pollfd,events不会被修改。

 

  ##select和poll存在的弊端:

    每次都需要将事件集在用户空间和内核之间来回拷贝,

    而且从内核返回的是整个事件集(包括就绪和未就绪),

    这将导致用户程序在索引事件时的时间复杂度为O(n)。

 

 

% epoll:

  使用epoll_event结构体,该结构体也将fd跟events绑定在一起。

  同时该结构体中的data结构体成员还有一个void* 结构体成员,可以绑   定一个事件处理函数。

  

% 最大支持的fd数量:

  % select所支持的最大fd数量往往有限制(修改会导致不可预期的后果)。

 

  % poll和epoll_wait由参数指定所指定所支持的最大fd数量,

    由 /proc/sys/fs/file-max指定(不同版本有不同的大小,通常是几万以上)

 

% 工作模式:

     select和poll只能工作在效率低下的LT模式(编程简单,但增加触发  次数),

     但epoll可以工作在高效的ET模式(减少了触发次数,程序必须保证在一次触发后处理完事件),还是工作在EPOLLONESHOT模式,进一步减少触发次数(可以实现在某个连接的数据可以被一个线程完整读取)。

 

 

  % 具体实现:

     select、poll采用轮询方式。每次调用都会扫面整个注册fd集合,并将就绪的返回。该实现方式的时间复杂度是O(n)。

   

epoll_wait则是采用回调方式。

  用户程序通过epoll_create创建一内核事件表(红黑树实现,可以快速插入,删除),

  通过epoll_ctl往该内核事件表注册事件,注册到内核事件表中的fd,会绑定一个回调函数(与网卡驱动程序相关),当fd有事件就绪时,会自动调用该回调函数,并将fd上的事件注册到内核就绪事件队列(双链表实现),

  用户程序调用epoll_wait,检测到内核就绪事件队列不为空时,将内核就绪事件队列的内容拷贝返回给用户。

  可见,epoll实现的事件复杂度为O(1)。

  通过回调实现的方式,

  适用于:连接数大,但活动连接不太大的场景。

         (但活动连接过大时,回调函数调用过于频繁,反而降低效率。)

抛砖引玉篇--IO复用技术(select、poll、epoll)

 

 

.