IO多路复用之poll、epoll和select区分

epoll和select

假设你在大学读书,住的宿舍楼有很多间房间,你的朋友要来找你。 select版宿管大妈就会带着你的朋友挨个房间去找,直到找到你为止。 而epoll版宿管大妈会先记下每位同学的房间号, 你的朋友来时,只需告诉你的朋友你住在哪个房间即可,不用亲自带着你的朋友满大楼找人。 如果来了10000个人,都要找自己住这栋楼的同学时,select版和epoll版宿管大妈,谁的效率更高,不言自明。 同理,在高并发服务器中,轮询I/O是最耗时间的操作之一,select和epoll的性能谁的性能更高,同样十分明了。

select的调用复杂度是线性的,即O(n)。举个例子,一个保姆照看一群孩子,如果把孩子是否需要尿尿比作网络IO事件,select的作用就好比这个保姆挨个询问每个孩子:你要尿尿吗?如果孩子回答是,保姆则把孩子拎出来放到另外一个地方。当所有孩子询问完之后,保姆领着这些要尿尿的孩子去上厕所(处理网络IO事件)。 还是以保姆照看一群孩子为例,在epoll机制下,保姆不再需要挨个的询问每个孩子是否需要尿尿。取而代之的是,每个孩子如果自己需要尿尿的时候,自己主动的站到事先约定好的地方,而保姆的职责就是查看事先约定好的地方是否有孩子。如果有小孩,则领着孩子去上厕所(网络事件处理)。因此,epoll的这种机制,能够高效的处理成千上万的并发连接,而且性能不会随着连接数增加而下降。

这里写图片描述 select单个进程可监视的fd数量受到限制 epoll和select都可以实现同时监听多个I/O事件的状态 epoll基于轮训机制,select基于操作系统支持的I/O通知机制 epoll支持水平触发和边沿触发两种模式

1 select

select本质上是通过设置或检查存放fd标志位的数据结构进行下一步处理。 这带来缺点:

  • 单个进程可监视的fd数量被限制,即能监听端口的数量有限 单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试 一般该数和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认1024个,64位默认2048。 alt

  • 对socket是线性扫描,即轮询,效率较低: 仅知道有I/O事件发生,却不知是哪几个流,只会无差异轮询所有流,找出能读数据或写数据的流进行操作。同时处理的流越多,无差别轮询时间越长 - O(n)。

当socket较多时,每次select都要通过遍历FD_SETSIZE个socket,不管是否活跃,这会浪费很多CPU时间。如果能给 socket 注册某个回调函数,当他们活跃时,自动完成相关操作,即可避免轮询,这就是epollkqueue

调用过程

alt (1)使用copy_from_user从用户空间拷贝fd_set到内核空间

(2)注册回调函数__pollwait

(3)遍历所有fd,调用其对应的poll方法(对于socket,这个poll方法是sock_poll,sock_poll根据情况会调用到tcp_poll,udp_poll或者datagram_poll)

(4)以tcp_poll为例,其核心实现就是__pollwait,也就是上面注册的回调函数。

(5)__pollwait的主要工作就是把current(当前进程)挂到设备的等待队列中,不同的设备有不同的等待队列,对于tcp_poll来说,其等待队列是sk->sk_sleep(注意把进程挂到等待队列中并不代表进程已经睡眠了)。在设备收到一条消息(网络设备)或填写完文件数据(磁盘设备)后,会唤醒设备等待队列上睡眠的进程,这时current便被唤醒了。

(6)poll方法返回时会返回一个描述读写操作是否就绪的mask掩码,根据这个mask掩码给fd_set赋值。

(7)如果遍历完所有的fd,还没有返回一个可读写的mask掩码,则会调用schedule_timeout是调用select的进程(也就是current)进入睡眠。当设备驱动发生自身资源可读写后,会唤醒其等待队列上睡眠的进程。如果超过一定的超时时间(schedule_timeout指定),还是没人唤醒,则调用select的进程会重新被唤醒获得CPU,进而重新遍历fd,判断有没有就绪的fd。

(8)把fd_set从内核空间拷贝到用户空间。

缺点

内核需要将消息传递到用户空间,都需要内核拷贝动作。需要维护一个用来存放大量fd的数据结构,使得用户空间和内核空间在传递该结构时复制开销大。

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
    • select支持的文件描述符数量太小了,默认是1024

2 poll

poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

  • 它将用户传入的数组拷贝到内核空间
  • 然后查询每个fd对应的设备状态:
    • 如果设备就绪 在设备等待队列中加入一项继续遍历
    • 若遍历完所有fd后,都没发现就绪的设备 挂起当前进程,直到设备就绪或主动超时,被唤醒后它又再次遍历fd。这个过程经历多次无意义的遍历。

没有最大连接数限制,因其基于链表存储,其缺点:

  • 大量fd数组被整体复制于用户态和内核地址空间间,而不管是否有意义
  • 如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd

3 epoll

可理解为event poll,epoll会把哪个流发生哪种I/O事件通知我们。所以epoll是事件驱动(每个事件关联fd)的,此时我们对这些流的操作都是有意义的。复杂度也降低到了O(1)。

3.1 触发模式

EPOLLLTEPOLLET两种:

  • LT,默认的模式(水平触发) 只要该fd还有数据可读,每次 epoll_wait 都会返回它的事件,提醒用户程序去操作,
  • ET是“高速”模式(边缘触发) alt 只会提示一次,直到下次再有数据流入之前都不会再提示,无论fd中是否还有数据可读。所以在ET模式下,read一个fd的时候一定要把它的buffer读完,即读到read返回值小于请求值或遇到EAGAIN错误

epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似回调机制激活该fd,epoll_wait便可收到通知。

EPOLLET触发模式的意义

若用EPOLLLT,系统中一旦有大量无需读写的就绪文件描述符,它们每次调用epoll_wait都会返回,这大大降低处理程序检索自己关心的就绪文件描述符的效率。 而采用EPOLLET,当被监控的文件描述符上有可读写事件发生时,epoll_wait会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait时,它不会通知你,即只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你。这比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符。

3.2 优点

  • 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)
  • 效率提升,不是轮询,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数 即Epoll最大的优点就在于它只关心“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll
  • 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
  • epoll通过内核和用户空间共享一块内存来实现的

表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现。

select和poll都只提供了一个函数——select或者poll函数。而epoll提供了三个函数,epoll_create,epoll_ctl和epoll_wait,epoll_create是创建一个epoll句柄;epoll_ctl是注册要监听的事件类型;epoll_wait则是等待事件的产生。

  • 对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。
  • 对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现中的第7步是类似的)。
  • 对于第三个缺点,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

4 总结

select,poll,epoll都是IO多路复用机制,即可以监视多个描述符,一旦某个描述符就绪(读或写就绪),能够通知程序进行相应读写操作。 但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

  • select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

  • select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

参考

  • Linux下select/poll/epoll机制的比较
  • select、poll、epoll之间的区别总结[整理]

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/873807.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

德国汉堡大学、清华大学联合英国布里斯托机器人实验室的研究工作分享:基于视觉遥操作的多指机械手灵巧操作

德国汉堡大学(张建伟院士团队)、清华大学(孙富春教授和方斌)联合英国布里斯托机器人实验室等单位在基于视觉信息遥操作的多指机械手灵巧操作研究方面取得进展。该工作得到了德国科学基金会(DFG)与中国国家自…

Git分支管理基本原理

原文全文详见个人博客: Git分支管理基本原理上文已讨论过svn分支管理的基本原理,本文将继续探讨Git分支管理的基本原理,以便后续进行进一步的理解和对比:https://www.coderli.com/git-branch-method/【Java学习交流(982860385)】…

Linux journalctl日志太长,如何倒序查看journalctl --reverse,journalctl -xeu

文章目录 需求实验方法一方法二 需求 Linux journalctl日志太长,如何倒序查看 我们通常关心的是最近的日志,但是每次打开日志都是按时间先后顺序显示的,如何倒序查看,请看下面: 实验 方法一 journalctl 命令默认按…

Linux工具相关介绍

目录 1.linux安装软件 2.Linux软件生态问题 3.linux软件包管理器yum 4.linux里面好玩的小命令 4.1安装源 4.2小火车 4.3人物说话情景 5.vim简单介绍 5.1简单认识 5.2代码编写 5.3命令模式 1.linux安装软件 1.1源代码安装:这个里面可能根据代码bug需要修改…

AI模型大比拼:Claude 3系列 vs GPT-4系列最新模型综合评测

AI模型大比拼:Claude 3系列 vs GPT-4系列最新模型综合评测 引言 人工智能技术的迅猛发展带来了多款强大的语言模型。本文将对六款领先的AI模型进行全面比较:Claude 3.5 Sonnet、Claude 3 Opus、Claude 3 Haiku、GPT-4、GPT-4o和GPT-4o Mini。我们将从性能…

huawei USG6001v1学习---防火墙高可靠性(双机热备)

1.什么是双机热备 如图:当左图的防火墙发生故障时,整个系统都会收到影响,而右图即使有防火墙发生故障,但是还有一台防火墙做备份,相对于只有一台防火墙,要可靠些。 由于防火墙上不仅需要同步配置信息&…

【数据分享】2013-2022年我国省市县三级的逐月SO2数据(excel\shp格式\免费获取)

空气质量数据是在我们日常研究中经常使用的数据!之前我们给大家分享了2000——2022年的省市县三级的逐月PM2.5数据和2013-2022年的省市县三级的逐月CO数据(均可查看之前的文章获悉详情)! 本次我们分享的是我国2013——2022年的省…

DAY05 CSS

文章目录 1 CSS选择器(Selectors)8. 后代(包含)选择器9. 直接子代选择器10. 兄弟选择器11. 相邻兄弟选择器12. 属性选择器 2 伪元素3 CSS样式优先级1. 相同选择器不同样式2. 相同选择器相同样式3. 继承现象4. 选择器不同权值的计算 4 CSS中的值和单位1. 颜色表示法2. 尺寸表示法…

java中多态的用法

思维导图: 1. 多态的概念 多态通俗的讲就是多种形态,同一个动作,作用在不同对象上,所产生不同的形态。 例如下图: 2. 多态的实现条件 Java中,多态的实现必须满足以下几个条件: 1. 必须在继承…

将mars3D导入自己的项目中

文章目录 一、打开自己的vite项目二、创建场景配置文件1.json文件路径 public\config\config.json2.创建组件定义文件路径 src\components\mars-work\mars-map.vue三、demo中引入四、必要样式 依赖文件 总结 一、打开自己的vite项目 我创建了一个新的 结构如下 二、创建场景…

Netty技术全解析:EventLoopGroup类详解

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

java学校--Object类方法--toString

第一点解析&#xff1a; 全类名就是包名加类名 getClass&#xff08;&#xff09;.getName()是得到其包名和类名如图&#xff0c;包名是com.hspedu.object_类名是Monster。 Integer.toHexString&#xff08;hashCode&#xff08;&#xff09;&#xff09;&#xff1b;是得到其…

Spring中@PostConstruct注解的使用

1.描述 1.1 背景 最近在做一个系统交互日志模块&#xff0c;要监控一个http请求&#xff0c;并记录请求与响应日志。项目中使用RestTemplate来发送http请求&#xff0c;所以打算给RestTemplate设置拦截器&#xff0c;来进行自定义操作。但是&#xff0c;只对当前类生效&#x…

Java文件IO操作知识点

关于文件 关于IO的定义 I:input输入 O:output输出,这是一套人为规定的规则,下面这个图方便我们理解输入输出的定义,统一以CPU为基准 在操作系统中其实把很多的资源都抽象成文件,我们在这里所说的文件就是"狭义"文件,平时保存在硬盘上的文件, 文件夹也是一种文件,我们…

「iOS」自定义cell与cell的复用

iOS学习 前言一&#xff1a;TableView协议二&#xff1a;cell的复用Cell的复用原理自定义cell总结 前言 UITableView是iOS开发中不可或缺的一部分&#xff0c;它是苹果iOS SDK提供的一种用于展示数据列表的视图控件。我们对其中的cell进行自定义&#xff0c;即可得到我们需要的…

Python 实现股票指标计算——BBI

BBI (Bull And Bear lndex) - 多空指标 1 公式 3日均价 3日收盘价之和 / 36日均价 6日收盘价之和 / 612日均价 12日收盘价之和 / 1224日均价 24日收盘价之和 / 24BBI (3日均价 6日均价 12日均价 24日均价) / 4 2 数据准备 我们以科创50指数 000688 为例&#xff0c…

Qt 学习第一天:安装VS和Qt

本人更换了一台新的电脑&#xff0c;使用联想电脑管家传输文件后没有安装VS&#xff0c;导致出现了以下问题&#xff1a; 疯狂查资料问人。弄了一个晚上加上午一个多小时之后&#xff0c;还是决定删掉原来的文件夹&#xff0c;重新下载一个 一、安装VS&#xff08;Visual Stud…

算法项目报告:物流中的最短路径问题

问题描述 物流问题 有一个物流公司需要从起点A到终点B进行货物运输&#xff0c;在运输过程中&#xff0c;该公司需要途径多个不同的城市&#xff0c;并且在每个城市中都有一个配送站点。为了最大程度地降低运输成本和时间&#xff0c;该公司需要确定经过哪些配送站点&#xff…

Android:requestLayout、invalidate 和 postInvalidate 的区别

提醒&#xff1a;下面源码来自SDK里Android-34版本 一、requestLayout 点击查看requestLayout官网文档 1.1 requestLayout方法源码 /*** Call this when something has changed which has invalidated the* layout of this view. This will schedule a layout pass of the v…

C++ :友元类

友元类的概念和使用 (1)将类A声明为B中的friend class后&#xff0c;则A中所有成员函数都成为类B的友元函数了 (2)代码实战&#xff1a;友元类的定义和使用友元类是单向的 (3)友元类是单向的&#xff0c;代码实战验证 互为友元类 (1)2个类可以互为友元类&#xff0c;代码实战…