1、select:
(1)作用:在一段指定时间内,监听用户感兴趣的文件描述符上的可读,可写和异常等事件;
(2)函数:
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* execptfds, struct timeval* timeout);
nfds:被监听的文件描述符的总数,通常被设置为select监听的所有文件描述符中的最大值+1(因为文件描述符是从0开始计数的);
readfds, writefds, execfds分别指向可读,可写和异常事件对应的文件描述符的集合;
timeout:设置超时时间,若传0,说明已经超时,则select立即返回,若传NULL,则select没有超时时间;
select调用返回时,内核将修改readfds, writefds, execfds(按位表示)来通知应用程序哪些文件描述符已经就绪;
fd_set结构体仅包含了一个整型数组,该数组的每个元素的每一位标记一个文件描述符,fd_set能容纳的文件描述符通过宏定义,一般是1024,限制了能同时处理的文件描述符的总量。
由于位操作过于繁琐,便使用下面的一系列宏来访问fd_set结构体中的位:
(3)函数返回值:
成功:返回就绪(可读/可写/异常)文件描述符的总数;
超时时间内没有任何文件描述符就绪,返回0;
失败:返回-1,并设置errno;
(4)缺点:
(i)单个进程可监视fd的数量有限制,即能监听的端口大小有限,(可以通过cat/proc/sys/fc/file_max查看监听的fd的数量);
一般默认的可监听fd数量最大值是:32位---1024 64位---2048
(ii)select的参数类型fd_set没有将文件描述符合事件绑定,仅仅是一个文件描述符集合,因此select需要提供3个这种了类型的参数来分别传入和输出可读、可写和异常事件,一方面,使得select不能处理更多类型的事件,另一方面,由于内核对fd_set集合的在线修改,使得应用程序下次调用select前必须重置这3个fd_set集合;
(iii)select调用返回整个用户关注的事件集合,包括就绪的和未就绪的,所以,应用程序要采用轮询的方式对就绪文件描述符进行索引,时间复杂度为O(n);
(iiii)需要维护一个用来存放大量fd的数据结构,会使用户空间和内核空间在传递该结构是开销大;
2、poll:
(1)作用:在指定时间内轮询一定数量的文件描述符,以测量其中是否有就绪者;
(2)函数:
int poll(struct pollfd* fds, nfds_t fds, int timeout);
nfds指定被监听的事件集合fds的大小;
timeout指定poll的超时时间,单位是毫秒;
(3)返回值:poll的返回值的select意义相同
(4)优点:
(i)没有最大连接数的限制,能达到系统允许打开的最大文件描述符,即65535;
(ii)poll的参数类型pollfd把文件描述符和事件都定义到其中,啮合每次修改的是pollfd的revents成员,events成员不变,所以调用poll时应用程序无需重置pollfd类型的事件集参数;
(5)缺点:
(i)大量的fd的数组被整体复制与用户态和内核地址空间之间,无论复制是否有意义;
(ii)当检测到fd上有事件发生并将此事件通知应用程序,若没有得到处理,下次调用还是会继续通知;
3、epoll(Linux特有):
(1)首先,epoll需要一组函数来完成任务。其次,epoll把用户关心的文件描述符上的事件放在内核的一个事件表中。但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。这个文件描述符使用如下函数来创建:
int epoll_create(int size);
该函数返回的文件描述符将用作其他所有epoll系统调用函数的第一个参数;
(2)操作内核事件表的函数:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
fd:要操作的文件描述符
op:指定操作类型,操作类型有如下3种:
EPOLL_CTL_ADD 往事件表中出则fd上的事件
EPOLL_CTL_MOD 修改fd上的注册事件
EPOLL_CTL_DEL 删除fd上的注册事件
event:指定事件
(3)epoll_wait函数:
epoll系列系统调用函数的主要接口是epoll_wait函数,它在一端超时时间内等待一组文件描述符上的事件,原型如下:
int epoll_wait(int epfd, struct epoll_event* events, int maxevent, int timeout);
timeout:和poll接口的timeout含义相同;
maxevents:指定最多监听多少个事件,必须大于0;
epoll_wait函数如果检测到事件,就将所有就绪的事件从内核事件表(由epfd参数指定)中复制到它的第二个参数events指向的数组中;
epoll_wait只返回就绪事件;
(4)优点:
(i)没有最大连接数的限制,能达到系统允许打开的最大文件描述符,即65535;
(ii)在内核中维护了一个事件表,可以直接从内核事件表中取得用户注册的事件;
(iii)epoll_wait系统调用的events参数仅用来返回就绪事件,是的应用程序索引(采用回调的方式)就绪文件描述符的时间复杂度达到O(1);
(iiii)epoll可以工作在ET模式下,比select和poll都更高效;
4、select,poll,epoll的区别: