概论:
select、poll和epoll三组I/O复用系统调用,这3组系统调用都能同时监听多个文件描述符。它们将等待由timeout参数指定的超时时间,直到一个或者多个文件描述符上有事件发生时返回.返回值是就绪的文件描述符的数量。返回0表示没有事件发生,超时.
我们从事件集、最大支持文件描述符的数量、工作模式和具体实现等四个方面进一步比较它们的异同.
事件集:
这三个函数都是通过某种结构体变量来告诉内核监听哪些文件描述符上的哪些事件,并使用该结构体类型的参数来获取内核处理的结果。
select模型:参数类型是fd_set,提供3个这样的结构来表示监听三种不同类型的事件可读、可写和异常.这使得select不能监听更多类型的事件。另外一方面由于内核对fd_set的输出结果进行修改,这使得select不得不在再次调用select时必须重置fd_set集合.
poll模型:把文件描述符和事件同时定义在一个结构体pollfd中。任何事件都统一处理,从而使得编程接口更加规范.并且内核每次返回修改的都是pollfd的revents成员,而events成员保持不变,所以下次调用poll不需重置.
但是select和poll的共同缺点:每次两者调用都需要返回整个用户注册的事件集.包括就绪的和未就绪的。这使得必须对整个事件集逐个遍历判断,时间复杂度O(n).
epoll模型采用了与select、poll完全不同的方式来管理用户注册的事件.它在内核中维护一个事件表,并提供一个独立的系统调用epoll_ctl来操作往其中添加、删除、修改事件。同时epoll_wait函数的events参数仅用来返回就绪的事件,这使得直接索引就绪文件描述符的事件复杂度O(1).
文件描述符的数量:
select能够同时监听最大的文件描述符数量是1024个,这使得往往对于大规模用户登录的服务器端明显不足。而poll和epoll相对没有限制,通常能够达到65535.
工作模式:
select、poll模型都只工作在相对低效的LT模式,而epoll可以工作在ET的高效模式.
实现原理:
select和poll采用的都是轮询的方式,即每次调用都是扫描整个注册的文件描述符集合,并返回其中就绪的文件描述符集。而epoll采用的回调的方式,内核检测到就绪的文件描述符时,将触发回调函数,回调函数就会将该文件描述符上对应的事件插入内核就绪事件队列.