I/O复用 - select、poll、epoll

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的区别:

猜你喜欢

转载自blog.csdn.net/niumengting/article/details/89737178