- 函数原型
int select(int nfds,struct fd_set *readfds,struct fd_set *writefds,struct fd_set * execptfds,struct timeval *timeout);
int poll(struct pollfd *fds,nfds_t fds,int timeout);
epoll:
int epoll_create(int size);
int epoll_ctl(int epollfd,int op,int fd,struct epoll_event *event);
int epoll_wait(int epollfd,struct epoll_event *events,int maxevents,int timeout); - 使用方面
select函数通过三个fd_set结构体变量分别给内核传递用户关注的所有可读,可写,异常事件,这使得select不能处理更多的事件类型,并且内核也通过这三个结构体变量返回就绪的文件描述符,所以每次使用之前,都必须重新设置这三个结构体变量;
poll函数将用户关注的文件描述符及其关注的事件,内核返回的文件描述符上发生的事件分离开表示,并且通过一个用户数组将所有的文件描述符传递给内核。因此,poll函数关注的事件类型更多,每次调用也不需要重新设置;
epoll是通过一组函数来完成的,epoll通过epoll_create创建一个内核事件表,通过epoll_ctl函数添加、删除、修改事件。epoll_wait只需要从内核事件表中读取用户的注册的事件。 - 使用限制
select所使用的fd_set结构实际上是一个整形数组,32bit系统上关注的文件描述符最多为1024个,最大文件描述符数1023;
poll和epoll分别用nfds和maxevents参数指定最多监听多少个文件描述符,这两个数值都能达到系统允许打开的最大文件描述符。 - 使用效率
select、poll每次调用都需要将用户空间的文件描述符拷贝到内核空间,epoll则直接从内核读取,效率更高;
select、poll每次都将所有的文件描述符(就绪的和未就绪的)返回,所以应用程序检索就绪文件描述符的时间复杂度为O(n),epoll通过events参数返回所有就绪的文件描述符,应用程序检索就绪文件描述符的时间复杂度为O(1)。
select、poll只能工作在效率较低的LT模式,而epoll则能工作在ET高效模式,并且epoll还支持EPOLLONESHOT事件,从而进一步减少事件被触发的次数。 - 内核效率
select和poll采用轮询的方式:即每次都需要扫描整个注册的文件描述符集合,并将其中就绪的文件描述符返回给用户程序,因此,内核中检测就绪文件描述符的算法时间复杂度为O(n),epoll则采用回调的方式,内核检测到就绪文件描述符,就触发回调函数,将文件描述符及发生的事件插入到内核就绪事件队列,因此,epoll在内核中检测就绪文件描述符的算法时间复杂度为O(1)。但是,当链接的活动比较频繁时,select和poll的效率比epoll要高,因为epoll的回调函数调用过程频繁,所以,epoll适用于链接较多,但是活动不频繁的情况。