I/O复用——select/poll/epoll区别

I/O复用

能使程序能同时监听多个文件描述。

1.select
在一段指定时间内,监听用户感兴趣文件描述符上的可读,可写和异常等事件。
函数原型:

int  select(int  nfds,  struct  fd_set *readfds, struct fd_set *writefds,  
            struct fd_set *execptfds,   struct  timeval * timeout);
nfds指定被监听的文件描述符总数,一般设置为select监听的所有文件描述符最大值加1;

select函数通过三个fd_set结构体变量readfds,writefds和execptfds,分别给内核传递用户关注的所有可读、可写、异常事件,
这使得select不能处理更多的事件类型,并且内核也通过这三个结构体变量返回就绪的文件描述符,所以每次使用之前,都必须重
新设置这三个结构体变量。fd_set结构体仅包含一个整型数组,数组的每个元素的每一位标记一个文件描述符。

timeout参数用来设置函数的超时时间。timeval结构体中long类型tv_sec成员(秒数)和tv_usec成员(微秒数),如果两者都
传递0,则select立即返回;如果传递NULL,则select将一直阻塞,直到某个文件描述符就绪。

select成功返回就绪文件描述符的总数。超时时间内没有任何文件描述符就绪,就返回0;失败返回-1并设置errno;接收到信号,
立即返回-1,并设置errno为EINTR。

2.poll系统调用
指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。

函数原型:

int  poll(struct pollfd * fds,  nfds_t  fds,   int  timeout);
fds参数是个pollfd结构类型的数组,指定所有感兴趣的文件描述符上发生的可读,可写和异常等事件。(poll事件类型如下图)
nfds是指定被监听事件集合fds的大小。
timeout参数指定poll的超时值。当timeout为-1时,poll调用永远阻塞,直到某个事件发生;当为0时,poll调用立即返回。

I/O复用——select/poll/epoll区别_第1张图片

3.epoll系统调用
epoll使用一组函数来完成任务;其次,epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中。但需要一个额外的文件描述符来标识内核中的事件表。

#include 
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);  

3.1 内核事件表

int  epoll_create(int  size);
size参数是告诉内核事件表的大小。该函数返回的文件描述符作为epoll系统调用的第一个参数,用来指定要访问的内核事件表。
int  epoll_ctl(int epollfd,int op,int fd,struct epoll_event* event);
此函数用来操作内核事件表。fd为要操作的文件描述符,op参数则指定操作类型。成功返回0;失败则返回-1并设置errno.

op操作类型如下:

  • EPOLL_CTL_ADD,往事件表中注册fd上的事件
  • EPOLL_CTL_MOD,修改fd上的注册事件
  • EPOLL_CTL_DEL,删除fd上的注册事件

    epoll_data_t是一个联合体,其中的fd是指定事件所从属的目标文件描述符。
    

3.2 epoll_wait函数:在一段超时时间内等待一组文件描述符上的事件。

如果监测到事件,就将所有就绪的事件从内核事件表(epfd指定)中复制到events指向的数组中。
int  epoll_wait(int epollfd, struct epoll_event *events, int  maxevents, int  timeout);  
该函数成功返回就绪的文件描述符个数;失败则返回-1并设置errno.
maxevents参数指定最多监听事件个数;
events用于输出epoll_wait检测到的就绪事件;
timeout参数指定poll的超时值。当timeout为-1时,poll调用永远阻塞,直到某个事件发生;当为0时,poll调用立即返回。

3.3 LT和ET模式

  • LT(Level Trigger,电平触发)模式
  • ET(Edge Trigger,边沿触发)模式

LT模式: 默认的工作模式,当epoll_wait检测到有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。
当下一次调用时,epoll_wait还会再次向应用程序通告此事件,直到事件被处理。

ET模式:当epoll_wait检测到有事件发生并将事件通知应用程序后,就立即处理该事件,因为后续的epoll_wait调用将不再
向应用程序通知这一事件。

ET模式在很大程度上降低了同一epoll事件被重复触发的次数,效率较高。

3.4 EPOLLONESHOT事件

    对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的可读,可写或异常事件,且只触发一次,除非使用
    epoll_ctl函数重置该文件描述符上注册的EPOLLONESHOT事件。注册了EPOLLONESHOT事件的socket一旦被某个线程处理完,
    该线程就立即重置socket上的EPOLLONESHOT事件,以确保这个socket下次可读时,EPOLLIN事件能被触发。

三者比较

1、使用方面

  select函数通过三个fd_set结构体变量分别给内核传递用户关注的所有可读、可写、异常事件,这使得select不能处理更多的事
  件类型,并且内核也通过这三个结构体变量返回就绪的文件描述符,所以每次使用之前,都必须重新设置这三个结构体变量。

   poll函数将用户关注的文件描述符以及其关注的事件、内核返回的文件描述符上发生的事件分离开表示,并且通过 一个用户数组
   将所有的文件描述符传递给内核。因此,poll函数能关注的事件类型更多,每次调用也不需要重新设置。

   epoll是通过一组函数来完成的,epoll通过epoll_create创建一个内核事件表,通过 epoll_ctl函数添加、删除、修改事件。
   epoll_wait只需要从内核事件表中读取用户的注册的事件。

2、使用限制

select所使用的fd_set结构实际上是一个整形数组,32bit系统上关注的文件描述符最多1024个,最大文件描述符数1023.

poll和epoll分别用nfds和maxevents参数指定最多监听多少个文件描述符,这两个数值都能达到系统允许打开的最大文件描述符数65535.

3、使用效率

1、select,poll每次调用都需要将用户空间的文件描述符拷贝到内核空间,epoll则只需要拷贝一次。效率更高。

 2、select、poll每次都将所有的文件描述符(就绪的和未就绪的)返回,所以应用程序检索就绪文件描述符的时间复杂度为O(n),
 epoll通过events参数返回所有就绪的文件描述符,应用程序检索就绪文件描述符的时间复杂度为O(1)。 

 3、select、poll只能工作在效率较低的LT模式,而epoll则能工作在ET高效模式,并且epoll还支持EPOLLONESHOT事件,从而进一
 步减少事件被触发的次数。 

5、内核效率

select和poll采用轮询的方式:即每次都需要扫描整个注册的文件描述符集合,并将其中就绪的文件描述符返回给用户程序,因此,
内核中检测就绪文件描述符的算法时间复杂度为O(n), 

epoll则采取回调的方式,内核检测到就绪文件描述符,就触发回调函数,将文件描述符及发生的事件插入内核就绪事件队列,因此,
epoll在内核中检测就绪文件描述符的算法时间复杂度为O(1)。 

但是,当链接的活动比较频繁是,select和poll的效率比epoll要高,因为epoll的回调函数调用过去频繁,所以,epoll适用于
链接较多,但是活动不频繁的情况。

你可能感兴趣的:(I/O复用——select/poll/epoll区别)