当前位置: 首页 > news >正文

【Linux】五种IO模型

文章目录

  • 1. IO基本概念
  • 2. 五种IO模型
    • 2.1 五个钓鱼的例子
    • 2.2 五种IO模型
      • 2.2.1 阻塞IO
      • 2.2.2 非阻塞IO
      • 2.2.3 信号驱动IO
      • 2.2.4 IO多路转接
      • 2.2.5 异步IO

1. IO基本概念

认识IO

IO就是输入和输出,在冯诺依曼体系结构中,将数据从输入设备拷贝到内存就叫输入,将输出将内存拷贝到输出设备就叫输出。
在这里插入图片描述

  • 对文件进行的读写操作本质就是一种IO,文件IO对应的外设就是磁盘。
  • 对网络进行的读写操作本质也是一种IO,网络IO对应的外设就是网卡。

OS如何得知外设中有数据可读?

输入就是操作系统将数据从外设拷贝到内存的过程,操作系统一定要通过某种方法得知特定外设上是否有数据就绪。

  • 并不是操作系统想要从外设中取数据时外设中就一定有数据,比如可能用户正在访问服务器,当用户的请求报文发出之后就需要在网卡中读取服务器发来的响应报文,但此时服务器可能还没有收到我们的报文,或是正在对收到的包文进行数据分析。
  • 但操作系统并不会去主动检测外设上是否有数据就绪,这样会降低操作系统的工作效率,因为大部分情况下外设当中都是没有数据的,如果操作系统去主动检测外设上是否有数据那么大部分情况下这样的操作都是徒劳的。
  • 操作系统实际采用的是中断的方式来得知外设上是否有数据就绪的,当某个外设上面有数据就绪时,该外设就会向CPU的中断控制器中发送中断信号,中断控制器再根据产生的中断信号的优先级顺序发送给CPU。
  • 每一个中断信号都有一个对应的中断处理程序,存储中断信号和中断处理程序映射关系的表叫做中断向量表,当CPU收到某个中断信号时就会自动停止正在运行的程序,然后根据该中断向量表执行该中断信号对应的中断处理程序,处理完毕后再返回被暂停的程序继续运行。

需要注意的是,CPU不直接和外设打交道指的是在数据层面上,而外设其实可以直接将某些控制信号发给CPU当中的某些控制器的。

OS如何处理从网卡中拿到的数据包?

操作系统任何时刻可能都会收到大量的数据包,因此操作系统必须将这些数据包管理起来。所谓的管理就是“先描述,再组织”,在内核当中有一个结构叫做sk_buff,该结构就是用来管理和控制接收或者发送数据包的信息的。

这是一个简化版的sk_buff结构:
在这里插入图片描述

  • 当操作系统从网卡中读取到了一个数据包之后,就会定义出一个sk_buff结构,然后用sk_buff结构当中的data指针指向这个读取到的数据包,并将这个定义出来的sk_buff结构与其他sk_buff结构用双链表的形式组织起来,这样操作系统对各个数据包的管理就变成了对双链表的增删查改了。
  • 接下来我们需要将读取上来的数据包交给最底层的链路层处理,进行链路层的解包和分用,此时就是让sk_buff结构当中的mac_header指针指向最初的数据包,然后向后读取链路层的报头,剩下的就是需要交给网络层的有效载荷了,此时便完成了链路层的解包。
  • 这时链路层就需要将有效载荷向上交付给网络层进行解包和分用了,这里所说的向上交付只是形象的说法,实际向上交付并不是要将数据从链路层的缓冲区拷贝到网络层的缓冲区,而是只需要让sk_buff结构当中的network_header指针指向数据包中链路层之后的数据即可,然后继续向后读取网络层的报头,便完成了网络层的解包。
  • 然后对传输层的处理同理,只需让sk_buff结构当中的transport_header指针指向数据包中网络层报头之后的数据,然后向后读取传输层的报头,便完成了传输层的解包。
  • 传输层解包之后就可以根据具体的使用的传输层协议,对应将剩下的数据宝贝到TCP或者UDP的接收缓冲区供用户读取即可。
    在这里插入图片描述
    发送数据时对数据进行封装也是同样的道理,就是依此在数据前面拷贝上对应的报头,最后再将数据发送出去(UDP)或者拷贝到发送缓冲区(TCP)即可。也就说,数据包在封装和解包的过程中,本质数据的存储位置是没有发送变化的,我们实际只是在用不同的指针对数据进行操作而已。

但内核中的sk_buff其实并没有那么简单,设计远比上面说的复杂。

  • 一方面,为了保证高效的网络报文处理效率,这就要求sk_buff的结构也必须高效。
  • 另一方面,sk_buff结构需要被内核协议当中的各个协议共同使用,因此sk_buff必须能够兼容所有的网络协议。

因此sk_buff结构其实是非常复杂的。

高效的IO

IO主要分两步:

  1. 第一步是等,等待IO就绪。
  2. 第二步是拷贝,将数据拷贝到内存或者外设。

任何IO的过程,都包含等和拷贝这两个步骤,在实际的应用场景中“等”消耗的时间远比“拷贝”消耗的时间多,因此要让IO变得高效,就要尽可能减少“等”的时间。

2. 五种IO模型

2.1 五个钓鱼的例子

IO的过程其实和钓鱼是非常类似的。

  • 钓鱼的过程同样分为“等”和“拷贝”两个步骤,只不过这里的“等”是等鱼上钩,“拷贝”指的是当鱼上钩后将鱼从河里“拷贝”到我们的鱼桶当中。
  • IO时等消耗的时间往往要比拷贝消耗的多,钓鱼也符合这个特点,钓鱼时哦我们大部分时间都在等待鱼上钩,而当鱼上钩后只需要一瞬间就能将鱼“拷贝”上来。

在了解五种IO模型之前,我们先看看什么样的钓鱼方式才是最高效的。

  1. 张三:拿了1个鱼竿,将鱼钩抛入水中就死死盯着浮漂,什么也不做,让有鱼上钩后再将鱼钓上来。
  2. 李四:拿了1个鱼竿,将鱼钩抛入水中后就去做其他事情,然后定期观察浮漂,如果有鱼上钩则挥动鱼竿将鱼钓上来,否则进行去做其他事情。
  3. 王五:拿了1个鱼竿,将鱼钩抛入水后在鱼竿顶部绑一个铃铛,然后就去做其他事情,如果铃铛响了就挥动鱼竿将鱼钓上来。
  4. 赵六:拿了00个鱼竿,将100个鱼竿抛入水中就定期观察这100个鱼竿的浮漂,如果某个鱼竿有鱼上钩则挥动对应的鱼竿将鱼钓上来。
  5. 田七:田七是一个有钱的老板,他给了自己的司机一个桶,一个电话,一个鱼竿,当司机去钓鱼,当鱼桶装满的时候再告诉田七来拿鱼,而田七自己则开车去做其他事情了。

张三、李四、王五的钓鱼效率是否一样?为什么?

本质都是一样的。

  • 首先它们的钓鱼方式都是一样的,都是等于上钩,然后再将鱼钓上来。
  • 其次,因为它们每个人都是拿的一根鱼竿。

因此他们三个的钓鱼效率本质是一样的,只不过他们的等待方式不同而已,张三是死等,李四是定期检测浮漂,王五是通过铃铛来判断鱼是否上钩。

需要注意的是,这里问的是他们的钓鱼效率是否一样,而不是问他们整体谁做的事更多,如果说整体做事情的量的话,那一定是王五做的最多,李四次之,张三最少。

与张三、李四、王五相比,赵六的钓鱼效率非常高。

高效的钓鱼就是要减少等的时间,增加拷贝的时间,赵六可以将等的时间重合,一次等待多个鱼竿有鱼上钩。

毫无疑问,赵六的钓鱼效率是四个人中最高的。

如何看待田七的钓鱼方式?

田七本人并没有参数整个钓鱼的过程,他只是发起了钓鱼的任务,真正钓鱼的是司机,田七在钓鱼期间可能就去做其他事情了。

如果将钓鱼看作是一种IO的话,前面四个人的钓鱼方式就是同步IO,田七的钓鱼方式就是异步IO。

2.2 五种IO模型

实际上上面所说的五种钓鱼方式分别对应五种IO模型

  1. 张三这种死等的方式叫做阻塞IO
  2. 李四这种定时检测的方式叫做非阻塞IO
  3. 王五这种通过设置铃铛的方式叫做信号驱动IO
  4. 赵六这种一次等待多个鱼竿的方式叫做IO多路转接
  5. 田七这种让别人帮自己钓鱼的方式就是异步IO

通过上面的例子我们可以看到,阻塞IO、非阻塞IO、信号驱动IO不能提高IO的效率,但是非阻塞IO和信号驱动IO能提高整体做事的效率。

其实,这个钓鱼场景中的各个事务都能与IO当中的相关概念对应起来,比如这里钓鱼的河对应就是内核,这里的每一个人都说进程或者线程,鱼竿对应的就是文件描述符或套接字,装鱼的桶对应的就是用户缓冲区。

2.2.1 阻塞IO

阻塞IO就是将内核将数据准备好之前,系统会一直等待。
在这里插入图片描述
阻塞IO是最常见的IO模型,所有的套接字,默认都是阻塞方式。

  • 比如调用recvfrom函数从某个套接字读取数据时,可能是底层数据还没有准备好,此时就需要等待数据就绪,当数据就绪之后再将数据从内核拷贝到用户空间,最后recvfrom函数才能返回。
  • 在recvfrom函数等待数据就绪期间,在用户看来该进程或者线程就阻塞住了,本质就是操作系统将该进程或线程的状态设置为了某种非R状态,然后将其放入等待队列当中,当数据就绪后操作系统再将其从等待队列中唤醒,然后该进程或线程再将数据从内核拷贝到用户空间。

以阻塞方式进行IO操作的进程或者线程,在“等”和“拷贝”期间都不会返回,在用户看来好像就是阻塞住了,因此我们称之为阻塞IO。

2.2.2 非阻塞IO

非阻塞IO就是,如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码。
在这里插入图片描述
非阻塞IO往往需要程序员以循环的方式反复尝试读写文件描述符,这个过程称为轮询,这对CPU来说是一种很大的浪费,一般只有在特定场景下才使用。

  • 比如当调用recvfrom函数以非阻塞方式从某个套接字上读取数据时,如果底层数据还没有准备好,那么recvfrom函数会立马错误返回,而不会让该进程或线程进行阻塞等待。
  • 因为没有读取的数据,因此该进程或线程后续还需要继续调用recvfrom函数,检测底层数据是否就绪,如果没有就绪数据,继续返回,直到就绪之后,再将数据从内核拷贝到用户空间然后成功返回。
  • 每次调用recvfrom函数读取数据时,不管底层有没有就绪,recvfrom函数都会直接返回,在用户看来该进程或线程就没有被阻塞住,称为非阻塞IO。

阻塞IO和非阻塞IO的区别在于,阻塞IO当数据没有就绪时,后续检测数据是否就绪是由操作系统发起的,而非阻塞IO当数据没有就绪时,后续检测数据是否就绪的工作是由用户发起的。

2.2.3 信号驱动IO

信号驱动IO就是内核将数据准备好的时候,使用SIGIO信号通知程序进行IO操作。
在这里插入图片描述
当底层数据就绪的时候会向当前进程或线程递交SIGIO信号,因此可以通过signal或者sigaction函数将SIGIO的信号处理程序定义为需要进行的IO操作,当底层数据就绪时就会执行对应的IO操作。

  • 比如我们需要调用recvfrom函数从某个套接字上读取数据,那么就可以将操作定义为SIGIO的信号处理程序。
  • 当底层数据就绪时,操作系统就会递交SIGIO信号,那么此时就会自动执行我们定义的信号处理程序,仅需将数据从内核拷贝到用户空间。

信号的产生是异步的,但信号驱动IO是同步IO的一种。

  • 我们说信号的产生是异步的,因为信号在任何时刻都可能产生。
  • 但信号驱动是同步IO的一种,因为当底层数据就绪时,当前进程或线程就需要停下正在做的事情,转而进行数据的拷贝操作,因此当前进程或线程仍然需要参与IO过程。

判断一个IO过程是同步的还是异步的,本质就是看当前进程或线程是否需要参与IO过程,如果要参与那么就是同步IO,否则就是异步IO。

2.2.4 IO多路转接

IO多路转接也叫IO多路复用,能够同时等待多个文件描述符的等待装填。
在这里插入图片描述
IO多路转接的思想:

  • 因为IO过程分为“等”和“拷贝”两个步骤,因此我们使用的recvfrom等接口的底层实际上都做了两件事,第一件事就是当数据不就绪时需要等,第二件事就是当数据就绪后需要进行拷贝。
  • 虽然recvfrom等接口也有等的能力,但这些接口一次只能等一个文件描述符上的数据或者空间就绪,这样IO效率太低了。
  • 因此系统为我们提供了三组接口,分别叫做select、poll、epoll,这些接口的核心工作就是等,我们可以将所有等的工作都交给这些多路转接的接口。
  • 因为这些多路转接接口是一次等多个文件描述符的,因此能够将等的时间进行重叠,当数据就绪之后再调用对应的recvfrom等函数进行数据的拷贝,此时这些函数就能够直接进行拷贝,而不需要再进行等操作了。

2.2.5 异步IO

异步IO就是数据拷贝完成之后,等待应用程序。
在这里插入图片描述

  • 进行异步IO需要调用一些异步IO的接口,异步IO接口调用后会立马返回,因为异步IO不需要你进行“等”和“拷贝”的操作,这两个操作都由操作系统完成,你要做的只是发起IO。
  • 当IO完成后操作系统会通知应用程序,因此进行异步IO的进程或线程并不参与IO的所有细节。

相关文章:

【Linux】五种IO模型

文章目录 1. IO基本概念2. 五种IO模型2.1 五个钓鱼的例子2.2 五种IO模型2.2.1 阻塞IO2.2.2 非阻塞IO2.2.3 信号驱动IO2.2.4 IO多路转接2.2.5 异步IO 1. IO基本概念 认识IO IO就是输入和输出,在冯诺依曼体系结构中,将数据从输入设备拷贝到内存就叫输入&am…...

SCT82A30DHKR_5.5V-100V Vin同步降压控制器

SCT82A30是一款100V电压模式控制同步降压控制器,具有线路前馈。40ns受控高压侧MOSFET的最小导通时间支持高转换比,实现从48V输入到低压轨的直接降压转换,降低了系统复杂性和解决方案成本。如果需要,在低至6V的输入电压下降期间&am…...

备忘录模式(C++)

定义 在不破坏封装性的前提下,捕获一-个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。 应用场景 ➢在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要…...

二叉排序树(二叉查找树)

二叉排序树(二叉查找树)的性质: 若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值。若它的右子树不为空,则右子树上所有结点的值均大于它的根将诶点的值。它的左、右子树也分别为二叉排序树。 对二叉…...

Python简单应用VII

题目 编程实现下述各题。 1.使用异常处理结构捕获多种可能的异常,如列表下标索引越界异常(IndexError)、试 图访问一个系统对象没有的属性所发生的异常(AttributeError)、读一个文件但该文件不存在。 2. 新建并打开文件stud1.txt,如果文件已存在就提示“…...

mysql--InnoDB存储引擎--架构和事务

MySQL进阶篇 文章目录 架构1、逻辑结构InnoDB 逻辑存储单元主层级关系图:1、表空间2、段3、区4、页5、行总结: 2、架构2、1 内存架构2、2 磁盘架构 3、事务3、1事务基础(1)事务(2)特性 架构 1、逻辑结构 I…...

0基础学习VR全景平台篇 第79篇:全景相机-泰科易如何直播推流

泰科易科技是中国的一家研发全景相机的高科技公司,前不久,在2020世界VR产业大会上发布了新一代5G VR直播影像采集终端--360starlight。以其出色的夜景成像效果和一“部”到位的直播方案重新定义了VR慢直播相机,对行业具有高度借鉴意义。 本文…...

代码调试4:实现退化模型的训练

代码调试:实现退化模型的训练 作者:安静到无声 个人主页 目录 代码调试:实现退化模型的训练问题1:如何在coco原始编码的基础上修改原始的文件?**方法1**:修改生成的文件**方法2**:直接修改源文件`instances_train2014.json`和`instances_val2014.json`问题2:构建退化后…...

8.7工作总结

一、我们想自定义一个titileBar出现如下这种情况,发现他原来的titileBar还未隐藏。 后来我尝试修改主题使得他没有主题noActionBar发现也不行,后来我参考原先我看过的项目使用了如下代码 this.getActionBar().hide();发现会报这个错误java.lang.NullPoi…...

数据库的约束 详解

一、约束的概述 1.概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。 2.目的:保证数据库中数据的正确、有效性和完整性。 3.分类: 约束描述关键字非空约束限制该字段的数据不能为nullNOT NULL唯一约束保证该字段的所有数据都是唯一、不…...

Tomcat 编程式启动 JMX 监控

通过这篇文章,我们可以了解到,利用 JMX 技术可以方便获取 Tomcat 监控情况。但是我们采用自研的框架而非大家常见的 SpringBoot,于是就不能方便地通过设置配置开启 Tomcat 的 JMX,——尽管我们也是基于 Tomcat 的 Web 容器&#x…...

Git rebase和merge区别详解

文章目录 变基的基础用法变基过程中的冲突解决冲突后无法push问题更新变基后的代码更有趣的变基用法变基的风险用变基解决变基变基 vs 合并 此文在阅读前需要有一定的git命令基础,若基础尚未掌握,建议先阅读这篇文章Git命令播报详版 在 Git 中整合来自不…...

JDK动态代理的原理解析、代码实现

代理就像是:买家(客户端)——销售(代理对象)——工厂(目标) 买家不用直接去工厂买,而是直接通过销售就可以购买到,假设工厂生产的是杯子,那么工厂只需要提供杯子,而销售在不改变杯子的生产过程的情况下对杯子进行包装设…...

理解和使用Ansible模块,简化自动化任务

Ansible是一款强大的自动化工具,用于管理和配置IT基础设施。在Ansible的世界中,模块(Module)是至关重要的组成部分。本文将深入探讨Ansible模块,了解它们如何简化自动化任务的执行过程。 Ansible模块是Ansible的核心组…...

Docker 快速安装 MinIO

概述 MinIO 是一款基于Go语言的高性能对象存储服务,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等。 拉取docker镜像 docker pull minio/minio创建宿主机数据目录(共享数据卷) 此…...

【源码分析】Nacos如何使用AP协议完成服务端之间的数据同步?

AP节点的同步使用的是异步任务消息队列的方式来实现的。 取出任务之后将会放入到一个List集合中。 然后会发现任务的执行是由条件的。 首先是当前集群的节点数量等于1000,那么此时会直接开始同步,当然这个条件在小项目中不会成立,所以还有…...

黑客删除服务器数据后,间谍软件制造商 LetMeSpy 关闭

总部位于波兰的间谍软件 LetMeSpy 已不再运行,并表示将在 6 月份的一次数据泄露事件中关闭其服务器,其中包括从数千名受害者手机中窃取的大量数据。 LetMeSpy 在其网站上以英语和波兰语发布的通知中确认该间谍软件服务已“永久关闭”,并将于 …...

ebay儿童书包产品CPC认证

儿童书包是一种能够盛放书本或者文具的包。现在的书包五花八门,以普通的布料或者是帆布等制成,有背带,包内一般分栏。一般分三种,背在身后的,挎在肩上的,轮式(可以拖行)的。 一、美国…...

Debezium系列之:增量快照初始化历史数据实际应用案例

Debezium系列之:增量快照初始化历史数据实际应用案例 一、需求背景二、查看数据库表数据三、使用增量快照采集历史数据四、初始化历史数据一、需求背景 采集数据库数据发送到Kafka Topic,供下游实时开发消费,在采集最新数据的同时,希望把历史数据也发送到Kafka Topic同时采…...

Transformer1.0-预热

一.Encoder encoder:译为编码器,负责将输入序列压缩成指定长度的向量,这个向量就可以堪称是这个序列的语义。然后可进行编码或特征提取等操作 在transformer中encoder由6个相同的层组成,每个层包含 Multi-Head Self-AttentionPosition-Wise …...

【探索Linux】—— 强大的命令行工具 P.2(Linux下基本指令)

前言 前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C的一些知识,也相信大家都掌握的不错,今天博主将会新开一个Linux专题,带领大家继续学习有关Linux的内容。今天第一篇文章博主首先带领大家了解一下…...

供应链售后服务自动化,利用软件机器人将数据整合提升效率

随着供应链管理的不断发展,售后服务的重要性也日益凸显。良好的售后服务不仅可以提高客户满意度,还能增强品牌形象和忠诚度。然而,传统的供应链售后服务往往存在繁琐的操作、低效率和易出错的问题。为了解决这一挑战,越来越多的企…...

VIM浅谈

VIM 1. 文件1.1 管理多个文件 仅以此篇纪念VIM作者Bram Moolenaar。 1. 文件 刚刚使用VIM,很多小伙伴疑惑的就是如何退出VIM,一顿乱按结果修改了文件,q又退不出去,还需要保存。这是因为文件是保存在磁盘里的,使用Vim…...

《深度探索c++对象模型》第六章笔记

非原创,在学习 6 执行期语意学(Runtime Semantics) 有这样一个简单的案例: if (yy xx.getValue()) {// ... } 其中,xx和yy的定义为: X xx; Y yy; class Y定义为: class Y { public:Y();~Y();bool operator(con…...

wolfSSL5.6.3 虚拟机ubuntu下编译运行记录(踩坑填坑)

网上相关教程很多(包括wolfSSL提供的手册上也是如此大而化之的描述),大多类似如下步骤: ./configure //如果有特殊的要求的话可以在后面接上对应的语句,比如安装目录、打开或关闭哪些功能等等 make make install 然后结束,大体…...

JAVA SE -- 第十六天

(全部来自“韩顺平教育”) IO流 一、文件 是保存数据的地方 2、文件流 文件在程序中是以流的形式来操作 流:数据在数据源(文件)和程序(内存)之间经历的路径 输入流:数据从数据…...

基于EIoT能源物联网的工厂智能照明系统应用改造-安科瑞黄安南

【摘要】:随着物联网技术的发展,许多场所针对照明合理应用物联网照明系统,照明作为工厂的重要能耗之一,工厂的照明智能化控制,如何优化控制、提高能源的利用率,达到节约能源的目的。将互联网的技术应用到工…...

docker-compose启动tomcat服务

docker-compose启动tomcat服务 编写docker-compose.yaml文件 version: "3.1" services:tomcat: restart: always image: tomcat:8.0.52 container_name: tomcat ports:- 8082:8080 environment:TZ: Asia/Shanghai volumes:- /usr/local/webapps/:/usr/local/t…...

10.多线程

文章目录 10.1简述线程、程序、进程的基本概念。以及他们之间关系是什么?10.2线程有哪些基本状态? 10.1简述线程、程序、进程的基本概念。以及他们之间关系是什么? 线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程…...

【有关数据库的编码格式和导出备份】

问题1:前端页面可以正常插入数据到数据库mysql中,但是却显示不了数据库中的数据内容? 分析:通过尝试,当数据插入的全部都是英文时,可以正常显示数据,但是出现中文时,则连带着全部数…...