select、poll、epoll三种I/O复用函数的对比

本文详细对比了Linux系统中的三种I/O复用函数:select、poll和epoll。select在处理大量文件描述符时效率较低,poll虽无数量限制但效率仍不高,而epoll利用内核事件表,实现了高效且低延迟的事件通知,特别适合高并发场景。epoll_wait通过返回就绪事件而非描述符集合,极大地提高了应用程序的性能。
摘要由CSDN通过智能技术生成

select、poll、epoll三种I/O复用函数的对比

select

select函数原型为:

int select( int nfds, fd_set* readfds, fd_set* writefds,  fd_set* exceptfds, struct timeval* timeout);
  1. nfds参数指定被监听的文件描述符总数,通常被设置为select监听的所有文件描述符的最大值加1。
  2. readfds、writefds、exceptfds参数分别指向可读、可写、异常等事件对应的描述符集合。应用程序调用select函数时通过这三个参数传入自己感兴趣的文件描述符,select调用返回时,内核将修改他们来通知应用程序哪些文件描述符已就绪。
    fd_set实质上是一个整型数组,该数组每一个元素的每一位(bit)标记一个文件描述符,通常能容纳的文件描述符数量为1024,但用户可以自己修改。
  3. timeout参数设置select的超时时间,timeval结构定义如下:
struct timeval{
	long tv_sec;   /* 秒数 */
	long tv_usec;  /* 微秒数 */
};

如果tv_sec和tv_usec都为0,则select将立即返回,若timeout为NULL,则select将阻塞,直到某个文件描述符就绪。

select函数执行成功时返回就绪文件描述符的总数,超时返回0,失败返回-1并设置errno。

poll

poll调用和select类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。poll函数原型如下:

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

//pollfd结构如下:
struct pollfd{
	int fd;           //文件描述符
	short events;	  //注册的事件
	short revents;	  //实际发生的事件,由内核填充
};
  1. fd成员指定文件描述符;events成员告诉poll监听fd上的什么事件,他是一系列事件的按位或;revents成员由内核修改,以通知应用程序fd上实际发生了哪些事件。
    常见的poll事件类型:
事件描述
POLLIN数据可读
POLLOUT数据可写
POLLERR错误(不能用作输入,只能用作输出)
  1. nfds指定被监听事件集合fds的大小
  2. timeout指定poll的超时时间,单位是毫秒。
  3. poll返回值含义与select相同。
    通常而言poll没有监听文件数量的限制。

epoll

epoll是Linux特有的I/O复用函数。他使用一组函数来完成功能。epoll把用户关心的文件描述符上的事件放在内核的一个事件表中,因此无需像select和poll一样每次调用都要重复传入文件描述符集或事件集,但epoll需要一个额外的文件描述符来唯一标识内核中的这个事件表。
epoll的三个函数如下:
epoll_create

int epoll_create( int size );
/*
size给内核一个提示,告诉它事件表需要多大。
该函数返回的文件描述符将作为其他epoll函数的第一个参数
*/

epoll_ctl

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

op参数指定操作类型,有以下三种操作类型:

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

event参数用来指定事件。
epoll_ctl运行成功时返回0,失败返回-1并设置errno。

epoll_wait

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

timeout参数同poll,maxevents参数指定最多监听的事件个数。
epoll_wait如果检测到事件,就将所有就绪的事件从内核事件表复制到第二个参数events指向的数组里,这个数组只用于输出epoll_wait检测到的就绪事件,而不像select和poll的参数那样既用于传入用户注册的事件又用于输出内核检测到的事件,这就极大的提升了应用程序索引就绪文件描述符的效率。

下面两段代码为poll和epoll使用上的区别

//索引poll返回的就绪文件描述符
int ret = poll( fds, MAX_EVENT_NUMBER, -1);
//遍历所有已注册文件描述符并找到其中的就绪者
for(int i = 0;i < MAX_EVENT_NUMBER;i++)
{
	if( fds[i].revent & POLLIN)  //判断第i个文件描述符的写文件事件是否就绪
	{
		int sockfd = fds[i].fd;  //处理socket
	}
}

epoll用法实例

int ret = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1);
//只需要遍历就绪的ret个文件描述符
for(int i = 0;i < ret;i++)
{
	int sockfd = events[i].data.fd;
}

从上面两段代码可以看出epoll的效率是高于poll的

select、poll和epoll的对比

系统调用selectpollepoll
事件集合用户通过3个参数分别传入感兴趣的可读、可写及异常等事件,内核通过对这些参数的在线修改来反馈其中的就绪事件。这使得用户每次调用select函数都要重置这三个参数统一处理所有事件类型,因此只需一个事件集参数。用户通过pollfd.events传入感兴趣的事件,内核通过修改pollfd.revent反馈其中的就绪事件内核通过一个事件表直接管理用户感兴趣的所有事件。因此每次调用epoll_wait时,无须反复传入用户感兴趣的事件。epoll_wait系统调用的参数events仅用来反馈就绪的事件
最大支持的文件描述符数一般为10246553565535
应用程序索引就绪文件描述符的效率O(n)O(n)O(1)
内核实现和工作效率采用轮询方式来检测就绪事件,算法复杂度为O(n)采用轮询方式来检测就绪事件,算法复杂度为O(n)采用回调方式来检测就绪事件,算法复杂度为O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值