select、poll、epoll的优缺点及底层实现

本文详细介绍了IO多路转接模型中的select、poll和epoll三种方式,包括各自的优缺点。select模型遵循POSIX标准,但存在描述符数量上限和性能下降的问题;poll模型没有数量限制,但仍有性能问题和不跨平台的缺点;epoll则解决了数量限制和性能问题,但不跨平台。

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

select模型

select优点

select缺点

poll模型

poll优点

poll缺点

epoll模型

epoll优点

epoll缺点


首先我们要知道,这三种都是IO多路转接模型

什么叫IO多路转接

所谓IO多路转接模型,就是指对大量描述符进行监控,大量描述符中有我们所关心的描述符就绪了,则调用返回,并且告诉哪些进程就绪了

select模型

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

参数解释:

参数nfds是需要监视的最大的文件描述符值+1;

rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合;

参数timeout为结构timeval,用来设置select()的等待时间

其中fd_set是一个整形数组,于其说是整形数组,倒不如说是位图,使用位图中对应的位来表示要监视的文件描述符

select工作流程,将fd加入监控集合前,我们首先将fd备份一份记作array,用于一会fd返回时作为源数据进行判断,然后将fd加入监控集合开始监控,当有描述就绪或者没有描述符就绪但是时间到了,则返回,返回之前会将监控集合中所有以前加入但是无事发生的fd清空掉,然后select从监控集合中进行轮询对比,找到所有就绪描述符,完成下重复下一次,要将array中所有描述符重新添加到监控集合中

select优点

 

  • select遵循posix标准,可以跨平台
  • select监控时间能精确到微秒

select缺点

  • select所能监控的描述符有上限,为1024和__fd_SETSIZE这个宏有关
  • select每次都需要将描述符集合拷贝到内核进行监控,设计用户态到内核态之间的数据拷贝
  • select在内核中对描述符集合进行轮询遍历,随着描述符增多性能而逐渐下降
  • select返回时会将所有未就绪描述符移除,所以下次使用又需要重新添加,编码变得更加复杂
  • select返回给用户态的集合,并没有告诉用户哪个描述符就绪了,仍需要用户对集合进行轮询判断,随着描述符增多而性能下降

poll模型

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

fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合.

nfds表示fds数组的长度.

timeout表示poll函数的超时时间, 单位是毫秒(ms).

用户为每一个关心的描述符定义事件结构,然后将描述符事件结构数组拷贝到内核中进行监控,轮询遍历事件数组中的描述符,判断描述符是否就绪了某个事件,若有描述符就绪了用户关心的事件,则会将就绪的事件放到事件结构的revents中,并调用返回,当调用返回后,用户遍历事件结构数组,判断结构中的revents事件中是否包含用户关心的事件,进而对描述符进行相应操作

poll优点

  • poll采用事件结构的方式对描述符进行监控,简化了多个描述符集合的监控编码流程
  • poll没有描述符数量的上限限制

poll缺点

  • poll每次监控都需要将所有的事件结构信息拷贝到内核,涉及用户态到内核态之间的数据拷贝
  • 在内核中poll进行就绪判断同样使用轮询遍历的判断,性能随着描述符增多而降低
  • 描述符事件就绪后,poll修改描述符事件结构中的revents信息为当前就绪的事件,但是poll并不会直接告诉用户哪一个事件就绪,仍需要用户对时间结构数组进行遍历,判断哪一个时间结构中的revents是用户关心的事件,进而对其操作
  • poll不能跨平台

epoll模型

每当一个进程调用epoll_create方法时,Linux内核就会创建一个eventpoll结构体,这个结构体中有两个成员,rdlist和rbr

struct eventpoll{  
   ....  
   /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/  
   struct rb_root rbr;  
   /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/  
   struct list_head rdlist;  
   ....  
};  

 

每个epoll对象都有这样一个独立的结构体,通过使用epoll_ctl方法向epoll对象中添加进来的事件,添加的事件都会挂载在rbr这颗红黑树上,所以插入的效率很高,同样的每个事件都会与程序建立回调关系,也就是说当响应的事件发生时会调用这个回调函数,这个回调函数会将发生的事件添加到rdlist双链表中,也就是说,用户只需要遍历这个双向链表就可以知道就绪的事件了

epoll优点

  •  监控的描述符数量无上限

  • 采用事件结构对描述符进行监控,简化了对多个描述符集合的监控操作流程
  • epoll的事件信息,每条信息只需要向内核拷贝一次
  • opoll是一个异步阻塞操作,操作系统对描述符事件进行监控,这个操作系统实现的监控采用事件回调的方式,不需要轮询遍历事件描述符集合,性能不会随着描述符增多而下降
  • 当描述符就绪后将事件信息添加到rdlist双向链表中,epoll_wait只需要看一下双向链表中是否为空就可以知道是否有描述符就绪和就绪的描述符是什么,避免了无谓的遍历操作

epoll缺点

  • epoll不能跨平台
  • 监控超时等待事件只能到毫秒
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值