I/O复用的方式之二poll和epoll

本文详细介绍了 Linux 下的 I/O 复用技术,包括 select、poll 和 epoll 的工作原理及性能对比。阐述了 epoll 的优势,如事件表管理和高效的工作模式,以及 LT 和 ET 模式的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来自《Linux高性能服务器编程》

一,poll系统调用和select系统调用类似,也是在一段时间内轮询看文件描述符上是否有时间就绪。

poll的原型如下:

#include<poll.h>

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

fds是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读可写异常事件。pollfd结构体的定义如下:

struct  pollfd

{ int  fd;  //文件描述符

  short events;  //注册的事件(每个位表示一个事件,与事件按位与,就能知道发生了哪个事件)

  short reevents;  //实际发生的事件,由内核填充(通知应用程序fd上实际发生了哪些事件)

};

poll支持的事件比较多,包括如下:

POLLIN数据可读(包括普通数据和优先数据)
POLLRDNORM普通数据可读
POLLRDBAND优先级带外数据可读(Linux不支持)
POLLPRI高级优先级数据可读,比如tcp带外数据
POLLOUT数据(包括普通数据和优先级数据)可写
POLLWRNORM普通数据可写
POLLWRBAND优先级带外数据可写
POLLRDHUPTCP连接被对方关闭,或者对方关闭了写操作
POLLERR错误码
POLLHUP挂起,比如管道的写端被关闭后,该端描述符上将收到POLLHUP事件
POLNVAL

文件描述符没有打开

     

      

 

 

 

 

 

 

 

 

 

 

nfds参数指定被监听事件集合fds的大小,类型nfds_t的定义如下:

typedef  unsigned  long  int  fnfds_n;

timeout指定超时时间,当timeout返回-1时,poll调用将永远阻塞,直到某个事件发生;当timeout返回为0时,poll调用将立即返回。

poll调用的返回值为就绪的文件描述符个数,返回0表示超时时间内没有文件描述符就绪,失败时返回-1. 

二,epoll是Linux特有的I/O复用,他在实现上和select和poll有很大差异。它使用一组函数完成任务,而不是单个函数。

为什么要epoll:select和poll有以下几点不足

每轮循环都需要从用户空间往内核空间拷贝数据;内核轮询检测索引就绪事件;I/O返回后需要遍历描述符找到有事件就绪的描述符。epoll解决了以上三个问题。

epoll把用户关心的文件描述符上的事件放在内核的一个事件表中,,从而无需像select和poll那样每次调用都要重复传入文件描述符集或事件集。但是epoll需要一个额外的文件描述符,来唯一标识内核中的这个事件。这个文件描述符由epoll_create函数来创建:

#include<sys/epoll.h>

int  epoll_create(  int  size);//由于内核事件表是一个树,所以这个size的大小无意义;

下面的函数来操作内核事件表:

#include<sys/epoll.h>

int   epoll_ctl( int  epfd,  int  op,  int  fd,  struct  epoll_event  *  event);

fd是要操作的文件描述符,op参数指定操作类型,操作类型有如下三种:

EPOLL_CTL_ADD:往事件表中注册fd上的事件;

EPOLL_CTL_MOD:修改fd上注册的事件;

EPOLL_CTL_DEL:删除fd上注册的时事件;

event参数指定事件,他是epoll_event结构指针类型,定义如下:

struct   epoll_event

{

_uint32_t   events;  //epoll事件

epoll_data_t   data;   //用户数据

};

epoll支持的事件类型与poll支持的事件类型基本相同,表示epoll事件的类型的宏就是在poll事件的宏前面加上E;但是epoll有两个额外的时间类型:EPOLLET和EPOLLONESHOT;

epoll_ctl成功时返回0,失败时返回-1.

epll体统调用的主要接口是epoll_wait函数,它在一段超时时间内等待一组文件描述符上的事件。原型如下:

#include<sys/epoll.h>

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

此函数成功返回就绪的文件描述符个数,失败时返回-1.

epfd指定内核事件表,maxevents参数指定最多监听的事件个数,必须大于零。events数组只用于输出epoll_wait检测到的就绪事件。

三,LT模式和ET模式

LT也叫电平触发模式,是默认的工作模式,在这种模式下epoll相当于一个效率较高的poll。当往epfd的内核事件表中注册一个EPOLLET事件时,epoll将以ET模式(边沿触发)来操作该文件描述符,ET模式是epoll的高效工作模式。select和poll都属于LT工作模式,epoll具有LT和ET两种模式。

简单来说,LT模式下,epoll_wait检测到文件描述符上面有事件就绪时,应用程序可以不立即处理该事件,这样,当应用程序下一次调用epoll_wait时,它还会向应用程序通知此事件,直到事件被处理为止。而ET模式下,epoll_wait将文件描述符上的就绪事件通知应用程序后,应用程序必须立即处理该事件,因为后续epoll_wait将不再通知。

需要注意的是,ET模式的文件描述符应该是非阻塞的,如果文件描述符没有设置非阻塞,那么读或写操作将会因为没有后续事件而一直处于阻塞状态。设置非阻塞用fcntl。

四,三组I/O函数的比较

系统调用selectpollepoll
事件集合用户通过三个参数分别传入感兴趣的可读可写异常事件,内核通过对这些函数的在线修改来反馈其中的就绪事件,这使得用户每次调用select都需要重置这三个参数统一处理所有事件类型,因此只需要传入一个事件集参数用户通过pollfd.events传入感兴趣的事件,内核通过修改pollfd.events反馈其中就绪的事件。内核通过一个事件表直接管理用户感兴趣的所有事件,因此每次调用epoll_wait时不需要反复传入。epoll_wait的events参数仅用来反馈就绪的事件。
应用程序索引就绪文件描述符的时间复杂度O(n)O(n)O(1)
最大支持文件描述符数一般有最大值限制6553565535
工作模式LTLT支持ET高效模式
内核实现和工作效率轮询检测就绪事件,O(n)轮询检测就绪事件,O(n)回调检测就绪事件, O(1)

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值