首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 系统运维 >

select、poll、epoll3组IO复用

2013-10-09 
select、poll、epoll三组IO复用int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,s

select、poll、epoll三组IO复用

int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout)//其中nfds是被监听的文件描述符总数通常设置为所有文件描述符中的最大值加一(是否可以理解为凡是小于这个描述符的都要被轮询?,加1是因为文件描述符从0开始计数),readfds、writefds、exceptfds分别指向可读可写异常事件对应的文件描述符集合(当事件发生时内核在线修改该集合以此来通知应用程序),timeout指出等待时间(能精确到微秒但是不可信)若为NULL则一直阻塞直到有事件发生,fd_set结构体能容纳的文件描述符数目由FD_SETSIZE限制(所有事件的数目)其同时限制了select能同时处理的文件描述符总量。调用成功返回就绪事件描述符总数失败返回-1。宏FD_SET(int fd,fd_set* fdset)将文件描述符fd和事件集合fd_set绑定(这个fd_set可以传给select中间的三个参数之一),宏FD_ISSET(int fd,fd_set* fdset)可以检测fd上的事件是否发生(内核在线修改这个数据机构所以select调用后可以使用该宏判断并处理相应事件),由于是内核在线修改事件集合所以每次select调用后都需要重置事件集合才能进行下一次select调用。因此select编写格式大致绑定文件描述符和事件集合,select调用,测试特定文件描述符上的事件是否发生,重新绑定文件描述符和事件集合的关系,再次调用select,伪代码如下:

epoll_event events[MAX_EVENT_NUMBER];//定义就绪事件集合(这里events不要和结构体epoll_event里的成员events混淆),该集合表示事件就绪后内核修改并由epoll_wait返回int epollfd=epoll_create(5);//创建事件表//这里将你需要监听的文件描述符的相应事件注册到事件表中for(int i=0;i<监听的文件描述符个数;i++){//假设监听的文件描述符存于数组fd中epoll_event event;//定义临时事件结构体event.data.fd=fd[i];//描述符fd[i]写到用户数据中event.events=EPOLLIN;//注册fd[i]是监听事件为可读,可以是其它事件按位或epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);//将该监听事件注册到事件表中}while(1){int ret=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1)//阻塞等待直到有事件就绪,events将返回就绪事件集合for(int i=0;i<ret;i++){//逐个处理就绪事件这里定义就绪事件对应的文件描述符events[i].data.fd上的数据处理逻辑}}//可见epoll返回的仅仅是就绪事件集合

三组IO复用的比较:

它们都是在等待timeout时间内返回就绪事件个数,select由于没有将文件描述符和事件集合自动绑定且内核在线修改就绪事件仍在同一事件集合中进行,所以再次调用select需要重新手动绑定文件描述符和事件集合,且只有三种事件集合可以绑定;poll提供了一个封装的结构体pollfd相对来说简洁些,当事件就绪后内核修改通知也在一个额外的参数revents内进行所以再次调用poll无需重新绑定文件描述符和事件集合的关系,不过select和poll每次返回的都是整个用户注册的事件集合所以应用程序索引就绪文件描述符的复杂度为O(n)//n是注册文件描述符个数;epolll则采用独立的事件表维护注册事件,采用epoll_ctl修改事件表,epoll_wait等待就绪事件且返回的就仅仅是就绪事件集合(采用独立参数),使得底层逻辑模块独立化,这使得应用程序索引就绪文件描述符的复杂度为O(1).

poll和epoll都采用额外参数指定监听文件描述符的个数(nfds和maxevents)这两个数值都能达到系统允许打开的最大文件描述符个数(65536),而select受限与FD_SETSIZE(貌似是1024忘记了...)

select和poll采用轮询机制每次调用都扫描整个注册文件描述符集合并将其中就绪的文件描述符返回给应用程序,所以它们检测就是事件的时间复杂度是O(n)。epoll_wait则采用事件回调函数当检测到就绪文件描述符时将触发回调函数,回调函数就将该文件描述符上对应的事件插入到内核就绪事件队列,时间复杂度为O(1)。当活动连接比较多的时候epoll_wait未必比select和poll效率高,因为回调函数过于频繁。所以epoll_wait适合连接数量多且idle连接较多(即活动连接少)的情形。

关于epoll的LT模式和ET模式及EPOLLINONESHOT参见前两篇文章。


热点排行