抛砖引玉篇--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)。
通过回调实现的方式,
适用于:连接数大,但活动连接不太大的场景。
(但活动连接过大时,回调函数调用过于频繁,反而降低效率。)
.