现在的位置: 首页 > 综合 > 正文

select,poll,epoll的区别及联系

2018年04月03日 ⁄ 综合 ⁄ 共 2097字 ⁄ 字号 评论关闭

select,poll,epoll的区别及联系

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

Select

 select的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),select使用描述字集,典型地是一个整数数组,其中每个整数中的每一位对应一个描述字。假设使用32位整数,那么该数组的第一个元素对应于描述字0~31,第二个元素对应于描述字32~63,依此类推。位数组的每一位代表其对应的描述符是否需要被检查。select的第二三四个参数表示需要关注读、写、错误事件的文件描述符位数组,这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件。所以每次调用select前都需要重新初始化fdset。timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间,NULL为阻塞操作。 

select对应于内核中的sys_select调用,sys_select首先将第二三四个参数指向的fd_set拷贝到内核,然后对每个被SET的描述符调用进行poll,并记录在临时结果中(fdset),如果有事件发生,select会将临时结果写到用户空间并返回;当轮询一遍后没有任何事件发生时,如果指定了超时时间,则select会睡眠到超时,睡眠结束后再进行一次轮询,并将临时结果写到用户空间,然后返回。select返回后,需要逐一检查关注的描述符是否被SET(事件是否发生)。

返回值:
(1)返回-1表示出错
(2)返回0表示没有文件描述符准备好,此时所有描述符都被清零,即超时。
(3)返回一个大于0的整数,该值为3个set中的文件描述符之和,如果同一个文件描述符同时读写准备好,那么在其返回值中将其计数为2。

 

 

Poll 

poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。

  poll的效率比select稍高(poll只遍历输入的监听数组中的描述符,如果数组中的fd<0,则poll忽略fd,当监听的描述符离散时效率稍高于select,比如监听0和1000两个句柄,则poll只需要遍历两个描述符,而select需要遍历1001个描述符;当监听描述符连续时,poll和select效率相当,底层实现也是一致的)。
poll和select共同的问题是性能较差:遍历所有的文件描述符,当监听描述符个数增加时,监听效率降低,并且select和poll每次都要在用户态和内核态拷贝监听的描述符参数。

 

Epoll

epoll通过epoll_create创建一个用于epoll轮询的描述符,通过epoll_ctl添加/修改/删除事件,通过epoll_wait检查事件,epoll_wait的第二个参数用于存放结果。 epoll与select、poll不同,首先,其不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的epoll描述符关联起来。另外epoll不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。epoll返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可,而不需要像poll、select那样进行轮询检查。

poll 解决了select和poll的几个性能上的缺陷:
(1)不限制监听的描述符个数(poll也是),只受进程打开描述符总数的限制;
(2)监听性能不随着监听描述符数的增加而增加,是O(1)的,不再是轮询描述符来探测事件,而是由描述符主动上报事件;
(3)使用共享内存的方式,不在用户和内核之间反复传递监听的描述符信息;
(4)返回参数中就是触发事件的列表,不用再遍历输入事件表查询各个事件是否被触发。epoll显著提高性能的前提是:监听大量描述符,并且每次触发事件的描述符文件非常少。epoll的另外区别是:①epoll创建了描述符,记得close;②支持水平触发和边沿触发。

抱歉!评论已关闭.