Linux之select、poll、epoll讲解

news2024/11/24 8:34:43

文章目录

  • 1 select、poll、epoll
    • 1.1 引言
    • 1.2 IO和Linux内核发展
      • 1.2.1 整体概述
      • 1.2.2 阻塞IO
      • 1.2.3 非阻塞IO
      • 1.2.4 select
      • 1.2.5 共享空间
      • 1.2.6 零拷贝
    • 1.3 select
      • 1.3.1 简介
      • 1.3.2 select缺点
    • 1.4 poll介绍
      • 1.4.1 与select差别
      • 1.4.2 poll缺点
    • 1.5 epoll
      • 1.5.1 epoll相关函数
      • 1.5.2 epoll优点

1 select、poll、epoll

1.1 引言

操作系统在处理io的时候,主要有两个阶段:

  • 等待数据传到io设备
  • io设备将数据复制到user space

我们一般将上述过程简化理解为:

  • 等到数据传到kernel内核space
  • kernel内核区域将数据复制到user space(理解为进程或者线程的缓冲区)

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

1.2 IO和Linux内核发展

1.2.1 整体概述

整体关系流程:
请添加图片描述

查看进程文件描述符:

获取pid进程号
ps -ef 
查看文件描述符
cd  /proc/进程号/fd ; ll

或者查看当前进程的fd  
$$ 表示 Shell 本身的 PID  (ProcessID)
cd  /proc/$$/fd ; ll

1.2.2 阻塞IO

计算机是有内核(kernel)的,内核向下连接很多的客户端,内核向上连接进程或线程,早先内核通过read命令读取文件描述符(fd),在这个时期socketblocking(阻塞的)BIO

如下图所示:线程通过内核读取文件fd8,读取到用户空间后,在通过内核写入文件fd9,如果fd8阻塞了,它会阻挡后面的操作
请添加图片描述

1.2.3 非阻塞IO

socket fd nonblock(非阻塞),进程/线程用一个,用循环遍历文件描述符(轮询发生在用户空间),这个时期是同步非阻塞时期NIO
这是由于内核socket本身就是nio,同步非阻塞IO
请添加图片描述

1.2.4 select

如果有1000个文件描述符fd,代表用户进程轮询调用1000次内核(kernel),造成成本很大的问题。于是在内核中增加了一个系统调用select,用户空间调用新的系统调用,统一将所有的文件描述符传给select,内核监控文件描述符的完成度,文件描述符完成之后返回,返回之后还有系统调用,再调用read(有数据的文件描述符),这个叫多路复用NIO,在这个时期,文件描述符考来考去成为累赘;
在这里插入图片描述

1.2.5 共享空间

共享空间是进程用户空间一部分,也是内核空间的一部分
引入一个共享空间mmap,将文件描述符放在共享空间里,文件描述符放在共享空间的红黑树里,将资源齐全的文件描述符放到链表
在这里插入图片描述

1.2.6 零拷贝

sendfile,是完成零拷贝的命令,两个参数一个写出io,一个读入io
在之前是先读取文件到用户空间,再写到内核中去,有了sendfile后,用这一个命令就可以了,不用读取写入
在这里插入图片描述

1.3 select

1.3.1 简介

单个进程就可以同时处理多个网络连接的io请求(同时阻塞多个io操作)。基本原理就是程序呼叫select,然后整个程序就阻塞状态,这时候,kernel内核就会轮询检查所有select负责的文件描述符fd,当找到其中那个的数据准备好了文件描述符,会返回给selectselect通知系统调用,将数据从kernel内核复制到进程缓冲区(用户空间)
在这里插入图片描述
下图为select同时从多个客户端接受数据的过程
虽然服务器进程会被select阻塞,但是select会利用内核不断轮询监听其他客户端的io操作是否完成
在这里插入图片描述

1.3.2 select缺点

select的几大缺点:

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小,默认是1024
  • select返回的是含有整个句柄的数组, 应用程序需要遍历整个数组才能发现哪些句柄发生了事件

1.4 poll介绍

1.4.1 与select差别

poll的原理与select非常相似,差别如下:

  • 文件描述符fd集合的方式不同,poll使用pollfd 结构而不是select结构fd_set结构,所以poll是链式的,没有最大连接数的限制
  • poll有一个特点是水平触发,也就是通知程序fd就绪后,这次没有被处理,那么下次poll的时候会再次通知同个fd已经就绪。

1.4.2 poll缺点

poll的几大缺点:

  • 每次调用poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 每次调用poll都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

1.5 epoll

1.5.1 epoll相关函数

epoll:提供了三个函数:

  • int epoll_create(int size);
    建立一个 epoll 对象,并传回它的id
  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    事件注册函数,将需要监听的事件和需要监听的fd交给epoll对象
  • int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    等待注册的事件被触发或者timeout发生

1.5.2 epoll优点

epoll解决的问题:

  • epoll没有fd数量限制
    epoll没有这个限制,我们知道每个epoll监听一个fd,所以最大数量与能打开的fd数量有关,一个g的内存的机器上,能打开10万个左右
    cat /proc/sys/fs/file-max可以查看文件数量
  • epoll不需要每次都从用户空间将fd 复制到内核kernel
    epoll在用epoll_ctl函数进行事件注册的时候,已经将fd复制到内核中,所以不需要每次都重新复制一次
  • selectpoll 都是主动轮询机制,需要遍历每一个fd
    epoll被动触发方式,给fd注册了相应事件的时候,我们为每一个fd指定了一个回调函数,当数据准备好之后,就会把就绪的fd加入一个就绪的队列中,epoll_wait的工作方式实际上就是在这个就绪队列中查看有没有就绪的fd,如果有,就唤醒就绪队列上的等待者,然后调用回调函数。
  • 虽然epoll 需要查看是否有fd就绪,但是epoll之所以是被动触发,就在于它只要去查找就绪队列中有没有fd,就绪的fd是主动加到队列中,epoll不需要一个个轮询确认。
    换一句话讲,就是selectpoll只能通知有fd已经就绪了,但不能知道究竟是哪个fd就绪,所以selectpoll就要去主动轮询一遍找到就绪的fd。而epoll则是不但可以知道有fd可以就绪,而且还具体可以知道就绪fd的编号,所以直接找到就可以,不用轮询。
  • 我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效

这个准备就绪list链表是怎么维护的呢?

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里;当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后就来把socket插入到准备就绪链表里了

​ 一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树就绪链表,执行epoll_ctl时,如果增加socket句柄,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可

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

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

相关文章

详解floor函数、ceil函数和round函数

1.floor函数 功能:把一个小数向下取整 即就是如果数是2.2 ,那向下取整的结果就为2.000000 原型:double floor(doube x); 参数解释: x:是需要计算的数 返回值: 成功:返回一个double类型的数,此数…

6-星际密码

题目 星际战争开展了100年之后,NowCoder终于破译了外星人的密码!他们的密码是一串整数,通过一张表里的信息映射成最终4位密码。表的规则是:n对应的值是矩阵X的n次方的左上角,如果这个数不足4位则用0填充,如…

C语言-自定义类型-结构体(11.1)

目录 思维导图: 1.结构体类型的基础知识 1.1结构体的声明 1.2特殊的声明 2.结构的自引用 3.结构体变量的定义和初始化 4.结构体内存对齐 4.1如何计算 4.2如何修改内对齐数 5.结构体传参 写在最后: 思维导图: 1.结构体类型的基础知…

Leetcode:98. 验证二叉搜索树(C++)

目录 问题描述: 实现代码与解析: 递归: 原理思路: 迭代(中序): 思路原理: 问题描述: 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。…

【目标检测】基于yolov6的钢筋检测和计数(附代码和数据集)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 Hello,大家好,我是augustqi。 今天给大家分享的目标检测项目是:基于yolov6的钢筋检测和计数实战项目(附代码和数据集…

如何成功发送一个Target 846 EDI报文?

Target塔吉特公司是仅次于沃尔玛的第二大零售百货集团,为客户提供当今时尚前沿的零售服务,物美价廉。而EDI(电子数据交换)是Target与供应商进行业务往来时要求使用的数据交换方式,具有安全可靠、高效和降低人工成本等优…

磨金石教育摄影技能干货分享|有哪些让你难以忘怀的人文摄影照片

在摄影分类中,人文摄影往往没有明确的释义。它既有纪实摄影的真实,又有艺术摄影的深奥。实际上,人文摄影可以说是二者的结合,在创意和表达上更倾向于艺术性,在画面上更有真实感。1 大雨滂沱这张肖像照极具张力&#xf…

智能家居给我们带来了什么?华秋携手信威安防传感器助力提升家居安全性

智能家居的出现,极大地方便了人们的生活,为生活提供便利舒适的体验;如同洗衣机与洗碗机解放了我们双手一样的道理,智能家居是在生活方方面面为我们了提供最大化的便利可能性。 那么,智能家居是如何为我们生活提供便利…

Jmeter@测试场景

目录 性能测试Jmeter,常用的场景 场景一:Thread Group 场景二、jpgc - Stepping Thread Group 场景三、jpgc - Ultimate Thread Group 场景一:Thread Group 参数配置-线程属性Thread Properties: 1.线程数(Number of Threads)…

并查集的使用

目录 一.介绍 二.并查集的实现 三路径压缩 四.相关题型 4.1省份数量 一.介绍 什么是并查集? 将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个 单元素集合,然后按一定的规律将归于同一组元素的集合合并。在这个过程中要…

十五、Express 中使用JWT进行登录验证

cookie 篇 : Node.js 中 cookie的验证登录 | session 篇 : Node.js 中 session验证登录 在前面讲过了两种验证登录的方式,其一是cookie,其二是session;那么在讲JWT之前先来简单的回顾这两种方式区别;cookie和sessi…

成功解决:npm 版本不支持node.js。【 npm v9.1.2 does not support Node.js v16.6.0.】

文章目录1、出现的问题2、查看自己的npm和node版本3、解决方法3.1 寻找对应的版本3.2 升级npm版本4、再次运行项目,成功5、如果上述方法失败,请看这里早起更新代码后,跑前端项目。结果噶了、跑不起来了;不慌,看看日志报…

JiBX 的实操

JiBX 的实操介绍基本概念BECL 介绍JiBX 基础概念开发jar 依赖BECLJiBXPOJO 实体类OrderAddressCustomerShippingOrderFactory 工厂类使用ant 来生成XML和POJO对象的绑定关系idea 使用antant 脚本 build.xmlant 添加 build.xmlbinding.xml报错问题解决测试TestOrder测试结果 如图…

C/C++ STL

常见容器性质总结 1.vector 底层数据结构为数组 ,支持快速随机访问 2.list 底层数据结构为双向链表,支持快速增删 3.deque 底层数据结构为一个中央控制器和多个缓冲区,详细见STL源码剖析P146,支持首尾(中间 不能&…

java mybatis的SpringBoot博客论坛管理系统

java mybatis的SpringBoot博客论坛管理系统 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…

Java数据结构:堆与PriorityQueue优先级队列的使用

文章目录1 什么是堆2 堆的实现思路2.1 大根堆的成员变量简介2.2 树的相关知识复习2.3 向下调整创建大根堆2.4 堆的插入2.5 堆的删除3 大根堆实现代码及测试4 PriorityQueue的使用4.1 特性简介4.2 常用方法4.3 使用PriorityQueue实现大根堆写在最后1 什么是堆 堆实质上就是对完全…

python对称加密AES的使用

python对称加密AES的使用 aes安装 pip install pycryptodome加密库引用 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import base64完整代码 from Crypto.Util.Padding import pad, unpad from Crypto.Cipher import AES import base64def A…

带你玩转OpenHarmony AI-基于海思NNIE的AI能力自定义

简介相信大家从玩转OpenAtom OpenHarmony(简称“OpenHarmony”)AI系列专题的其他文章中,已经拓展了OpenHarmony AI在智慧出行、智慧办公等多场景下的AI自动检测、识别和判断的新体验,对于OpenHarmony平台上的AI开发有了一定认识。…

通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 (史上最全)

事件总线在 进行JAVA模块解耦 ,价值巨大 实际开发中,常常 通过事件总线EventBus/AsyncEventBus进行JAVA模块解耦 , 比如,在顶级开源组件 hotkey的源码中, 就多次用到 EventBus/AsyncEventBus进行JAVA模块解耦 所以&am…

71、AdaNeRF: Adaptive Sampling for Real-time Rendering of Neural Radiance Fields

简介 官网:https://thomasneff.github.io/adanerf/ 新的双网络架构,它采用正交方向,通过学习如何最好地减少所需样本点的数量,将网络分为联合训练的 sample 和 shading 网络,训练方案在每条射线上采用固定的样本位置…