五种IO模型

news2025/7/14 22:55:19

文章目录

  • 什么是IO
  • 操作系统的IO
  • 五种IO模型
    • 阻塞IO
    • 非阻塞IO
    • 多路转接IO(复用IO)
    • 信号驱动IO
    • 异步IO
  • 同步异步

什么是IO

IO,即input/output,IO模型即输入输出模型,而比较常见且听说的便是磁盘IO,网络IO.

按照冯诺依曼结构的来看,假设我们把运算器、控制器、存储器三个设备看做一个整体(假设称为中转),那么输入设备、输出设备,和中转就构成一个中转IO,也就是说IO是以某一个核心为主体的,而涉及计算机核心与其他核心之间数据迁移的过程我们就成为一个IO.

在这里插入图片描述


操作系统的IO

如果要将内存中的数据写入到磁盘,那么其IO主体是什么呢?主体很可能是一个应用程序.

但我们电脑上所跑起来的应用程序是无法直接进行一些特殊操作(IO)的,比如内存读写,磁盘读写,因为用户可能会利用此程序直接或者间接的对计算机造成破坏,应用程序想要进行这些特殊操作,只能交给底层软件—操作系统.也就是说应用程序想要将数据从内存写入磁盘或者反过来说从磁盘读取内存,只能通过操作系统对上层开放的API来进行.

而在任何一个应用程序里面,都会有进程地址空间,该空间分为两部分,一部分称为用户空间(允许应用程序进行访问的空间),另一部分称为内核空间,是只能留给操作系统进行访问的空间,也就是说它受到保护.

因此,一个应用程序想要进行一次IO操作将会分为两个阶段:

  • IO调用:应用程序进程向操作系统内核发起调用。
  • IO执行:操作系统内核完成IO操作

而操作系统完成一次IO操作也包括两个过程:

  • 准备数据阶段:内核等待I/O设备准备好数据
  • 拷贝数据阶段:将数据从内核缓冲区拷贝到用户进程缓冲区

在这里插入图片描述

因此一个完整的IO过程包括以下几个步骤:

  1. 应用程序进程向操作系统发起IO调用请求
  2. 操作系统准备数据,把IO外部设备的数据,加载到内核缓冲区
  3. 操作系统拷贝数据,即将内核缓冲区的数据,拷贝到用户进程缓冲区

而一次IO的本质其实就是: 等待 + 拷贝

五种IO模型

在了解IO模型之前,我们先回顾一下网络应用程序之间,是怎么进行数据发送和接收的.按照一下两个应用程序为例:
在这里插入图片描述

应用A把消息发送到 TCP发送缓冲区,TCP发送缓冲区再把消息发送出去,经过网络传递后,消息会发送到B服务器的TCP接收缓冲区,B再从TCP接收缓冲区去读取属于自己的数据,同理,当B对A发送数据时,路径类似.

现在我们看下什么是IO模型

阻塞IO

参考上图流程,我们思考一个问题,TCP缓冲区还没有完全接收(假设数据量为1,目前接收数据量为0或小于1)到属于应用B该读取的消息时,应用B向TCP缓冲区发起读取申请,TCP接收缓冲区是应该马上告诉应用B 现在没有你的数据,你去做别的事吧,还是说让应用B在这里等着,直到有数据再把数据交给应用B。

在这里插入图片描述

同理,应用A在向TCP发送缓冲区发送数据时,如果TCP发送缓冲区已经满了,那么是告诉应用A现在没空间了,还是让应用A等待着,等TCP发送缓冲区有空间了再把应用A的数据访拷贝到发送缓冲区。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWd88p6W-1668785543910)(13五种IO模型.assets/image-20221118225747153.png)]

而阻塞IO就是当应用B发起读取数据申请时,在内核数据没有准备好之前,应用B会一直处于等待数据状态,直到内核把数据准备好了交给应用B才结束。

学术语言就是:在应用调用recvfrom读取数据时,其系统调用直到数据包到达且被复制到应用缓冲区中或者发送错误时才返回,在此期间一直会等待,进程从调用到返回这段时间内都是被阻塞的称为阻塞IO;在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式

流程和流程图:

流程描述:

1、应用进程向内核发起recfrom读取数据

2、内核进行准备数据报(此时应用进程阻塞)

3、内核将数据从内核负复制到应用空间。

4、复制完成后,返回成功提示

在这里插入图片描述

代码模拟阻塞IO过程

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main(){
    char buf[1024];
    printf("等待您的数据准备:");fflush(stdout);
    ssize_t Rea = read(0,buf,sizeof(buf)-1);  //等待键盘键入数据
    if(Rea>0){
        buf[Rea]=0;
        printf("数据准备完毕,开始写进屏幕\n");
        sleep(1);
        write(1,buf,strlen(buf));
    }
    return 0;
}

非阻塞IO

非阻塞IO就是当应用B发起读取数据申请时,如果内核数据没有准备好会即刻告诉应用B,不会让B在这里等待,如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码;

非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对CPU来说是较大的浪费, 一 般只有特定场景下才使用

流程和流程图:

1、应用进程向内核发起recvfrom读取数据。

2、内核数据报没有准备好,即刻返回EWOULDBLOCK错误码。

3、应用进程再次向内核发起recvfrom读取数据。

4、内核倘若已有数据包准备好就进行下一步骤,否则还是返回错误码,执行第三步骤

5、内核将数据拷贝到用户空间。

6、完成后,返回成功提示。

在这里插入图片描述


fctnl函数用来对文件描述符进行处理,他有五种功能:

//函数声明
int fcntl(int fd, int cmd, ... /* arg */ );
  • 复制一个现有的描述符(cmd=F_DUPFD).

  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).

  • 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).

  • 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).

  • 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

我们此处只是用第三种功能, 获取/设置文件状态标记, 就可以将一个文件描述符设置为非阻塞

void SetNonBlock(int fd) {
    int fl = fcntl(fd, F_GETFL);  
    if (fl < 0) {
    	perror("fcntl");  return;
    }
	fcntl(fd, F_SETFL, fl | O_NONBLOCK);  //设置为非阻塞
}

代码模拟非阻塞:

int main()
{
    char buf[1024];
    SetNonBlock(0);
    while (true)
    {
        ssize_t s = read(0, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            write(1,buffer,strlen(buffer));
            printf("读取成功!");
        }
        else
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                sleep(1);
                std::cout << "当前没有出错,仅仅底层数据没有就绪罢了..." << std::endl;
                continue;
            }
            if(errno == EINTR){
                std::cout << "读取被信号中断" << std::endl;
                continue;
            }
            std::cout << "read error: " << s << std::endl;
            break;
        }
    }
    return 0;
}

多路转接IO(复用IO)

假设在并发的环境下,可能会N个人向应用B发送消息,这种情况下我们的应用就必须创建多个线程去读取数据,每个线程都会自己调用recvfrom 去读取数据。那么此时情况可能如下图:
在这里插入图片描述

倘若服务器在上百万请求下,应用B就需要创建上百万的线程去读取数据,同时又因为应用线程是不知道数据是否准备好,为了保证消息能及时读取到,那么这些线程自己必须不断的向内核发送recvfrom 请求来读取数据,而如此庞大的线程资源仅仅只是用来等待读取数据,那么就意味着能做其它事情的线程就会少,这将造成极其庞大的浪费.

多路转接模型: 所以有人就提出了一个思路,由一个线程监控多个网络请求(我们后面将称为fd文件描述符,linux系统把所有网络请求以一个fd来标识),来完成数据状态询问的操作,当有数据准备就绪之后再分配对应的线程去读取数据,这样就可以节省出大量的线程资源出来

在这里插入图片描述

上图可以看出多路复用就是系统提供了一种函数可以同时监控多个fd的操作,这个函数就是我们常说到的select、poll、epoll函数,可以通过它们同时监控多个fd,只要有任何一个数据状态准备就绪了,就返回可读状态,这时询问线程再去通知处理数据的线程,对应线程此时再发起recvfrom请求去读取数据.

虽然从流程图上看起来和阻塞IO类似.,实际上最核心之处在于IO多路转接能够同时等待多个文件 描述符的就绪状态,来达到不必为每个fd创建一个对应的监控线程,从而减少线程资源创建的目的。

流程图:

在这里插入图片描述

信号驱动IO

多路转接解决了一个线程可以监控多个fd的问题,但是select采用无脑的轮询就显得有点暴力,因为大部分情况下的轮询都是无效的,所以有人就想,别让我总去问数据是否准备就绪,而是等你准备就绪后主动通知我,这边是信号驱动IO.

信号驱动IO是在调用sigaction时候建立一个SIGIO的信号联系,当内核准备好数据之后再通过SIGIO信号通知线程,此fd准备就绪,当线程收到可读信号后,此时再向内核发起recvfrom读取数据的请求,因为信号驱动IO的模型下,应用线程在发出信号监控后即可返回,不会阻塞,所以一个应用线程也可以同时监控多个fd。

在这里插入图片描述

内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作.

在这里插入图片描述

多路转接IO里面的select虽然可以监控多个fd了,但select其实现的本质上还是通过不断的轮询fd来监控数据状态, 因为大部分轮询请求其实都是无效的,所以信号驱动IO意在通过这种建立信号关联的方式,实现了发出请求后只需要等待数据就绪的通知即可,这样就可以避免大量无效的数据状态轮询操作。

异步IO

多路转接IO和信号驱动IO虽然相比于单纯的阻塞IO和非阻塞IO来说,提升了一些效率,但它们都有着两次操作,即先发送select让其等待,然后再recv进行读取,但我们网络需求是直接读取,所以有人在前面基础上提出了一种方法:

应用只需要向内核发送一个读取请求,告诉内核它要读取数据后即刻返回;内核收到请求后会建立一个信号联系,当数据准备就绪,内核会主动把数据从内核复制到用户空间,等所有操作都完成之后,内核会发起一个通知告诉应用,我们称这种模式为异步IO模型

在这里插入图片描述

异步IO的优化思路是解决应用程序需要先后发送询问请求、接收数据请求两个阶段的模式,在异步IO的模式下,只需要向内核发送一次请求就可以完成状态询问和数拷贝的所有操作

同步异步

同步和异步关注的是消息通信机制.

所谓同步,就是在发出一个调用时,自己需要参与等待结果的过程,则为同步,前面四个IO都自己参与了,所以也称为同步IO.

异步IO,则指出发出调用以后,到数据准备完成,自己都未参与,则为异步.

另外, 在讲多进程多线程的时候, 也有提到同步和互斥. 这里的同步和今天所讲的同步异步是完全不相干的概 念.

进程/线程同步:是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、 传递信息所产生的制约关系.,尤其是在访问临界资源的时候.

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

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

相关文章

Kruskal算法求最小生成树

输入样例&#xff1a; 4 5 1 2 1 1 3 2 1 4 3 2 3 2 3 4 4输出样例&#xff1a; 6适用于稀疏图&#xff0c;快&#xff1b; 实现步骤&#xff1a; 1.将所有边将权重从小到大排序&#xff1b;sort&#xff1b;O&#xff08;mlogm&#xff09; 2.枚举每条边a,b&#xff1b;权…

RabbitMQ的 AMQP协议都是些什么内容呢

之前也讲述过关于 RabbitMQ 的相关内容&#xff0c;比如他们的配置&#xff0c;以及 RabbitMQ 整合 SpringBoot 使用&#xff0c;而且自己使用过之后&#xff0c;就会在自己的简历上面写上自己使用 RabbitMQ 实现了什么功能&#xff0c;但是这就会导致&#xff0c;有些面试官就…

QPainter、QPen 、QBrush(概念)

Qt中的三大绘画类&#xff1a; QPainter :进行绘画QPaintDevice :提供画图设备&#xff0c;是一个二维的抽象&#xff08;是所有可绘制对象的基类&#xff09;QPaintEngine :提供了画家用于绘制到不同类型的设备上的界面QPainter(画家) QPainter 提供高度优化的功能来完成 GUI …

Presto 聚合中groupBy分组的实现

一.前言 本文只要探索在Presto中groupby是怎么实现的。在Preso中&#xff0c;groupby的分组主要通过对数据Hash的数值比较进行分组&#xff0c;其中有2种情况&#xff0c;一直是仅有一个groupby字段而且字段是Bigint类型的&#xff0c;此场景下会使用BigintGroupByHash来实现分…

Spring boot使用ProGuard实现代码混淆

目录参考一、 ProGuard简介二、混淆配置要点三、快速开始方案一 配置文件新增proguard.cfg配置插件打包方案二 pom中定义配置参考 Spring boot使用ProGuard实现代码混淆 SpringBoot 玩一玩代码混淆&#xff0c;防止反编译代码泄露 代码混淆常见于安卓的apk安装文件, 服务端的…

11月千言最新评测推荐,覆盖中文对话、视频语义理解、可信AI等前沿方向

千言数据集是百度联合中国计算机学会、中国中文信息学会共同发起的数据共建计划&#xff0c;千言针对每个自然语言处理问题&#xff0c;均收集和整理多个开源数据集&#xff0c;进行统一的处理并提供统一的测评方式&#xff0c;帮助加速模型的研发。截至目前&#xff0c;千言评…

[附源码]java毕业设计上海景宏不锈钢厨房设备报修系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

云原生系列 【轻松入门容器基础操作】

✅作者简介&#xff1a; CSDN内容合伙人&#xff0c;全栈领域新星创作者&#xff0c;阿里云专家博主&#xff0c;华为云云 享专家博主&#xff0c;掘金后端评审团成员 &#x1f495;前言&#xff1a; 最近云原生领域热火朝天&#xff0c;那么云原生是什么&#xff1f;何为云原生…

数据分析 | Pandas 200道练习题 进阶篇(3)

文章目录DA21 大佬用户成就值比例DA22 牛客网用户最高的正确率DA23 统计牛客网用户的名字长度DA24 去掉信息不全的用户DA25 修补缺失的用户数据DA26 解决牛客网用户重复的数据总结&#xff1a;❤️ 作者简介&#xff1a;大家好我是小鱼干儿♛是一个热爱编程、热爱算法的大三学生…

生信步骤|MAFFT结合HMMER进行多序列比对和基于隐马模型的基因搜索

蛋白质都是由相似的小型结构域组成的。如果我们有若干个已知的蛋白序列&#xff0c;那我们就可以根据这些蛋白序列比较其含有的保守域&#xff0c;寻找在蛋白数据库中上是否也有一样保守域的蛋白。而后根据统计学模型&#xff0c;将显著性较高的蛋白序列预测为同一类基因家族蛋…

Oracle SQL执行计划操作(5)——分区相关操作

5. 分区相关操作 该类操作与SQL语句执行计划中分区表操作相关。根据不同的具体SQL语句及其他相关因素&#xff0c;如下各操作可能会出现于相关SQL语句的执行计划。 1&#xff09;PARTITION RANGE ALL 对范围分区&#xff08;RANGE PARTITION&#xff09;表的所有分区进行子…

内存泄漏检测C版小工具

一 内存泄漏简介 内存泄漏&#xff08;Memory Leak&#xff09;是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放&#xff0c;造成系统内存的浪费&#xff0c;导致程序运行速度减慢甚至系统崩溃等严重后果。 内存泄漏分类&#xff1a; 1.堆内存泄漏&#xff1…

基于LMI的非线性混沌系统滑模控制

目录 前言 1.非线性系统 2.控制器设计 3.仿真分析 3.1仿真混沌系统 3.2 LMI求解反馈阵F 3.3仿真模型 ​​​​3.4仿真结果 3.5注意事项 前言 前面我们介绍了很多种滑模面设计&#xff0c;以及介绍了几篇结合LMI的滑模控制&#xff0c;其核心思想可以看作是用LMI去控制…

【python与数据分析】Numpy数值计算基础——补充

目录 二、矩阵生成与常用操作 1.生成矩阵 2.矩阵转置 3.查看矩阵特征 4.矩阵乘法 5.计算相关系数矩阵 6.计算方差、协方差、标准差 7.行列扩展 8.常用变量 9.矩阵在不同维度上的计算 10.应用 &#xff08;1&#xff09;使用蒙特卡罗方法估计圆周率的值 &#xff0…

【Transformers】第 10 章 :从零开始训练 Transformer

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

JS实现复制富文本到剪贴板/粘贴板的最佳实践

背景 最近有想实现一个功能&#xff0c;通过点击一个button按钮&#xff0c;来复制网页内容(含html)来实现复制后粘贴到邮件或者word具有富文本的效果。在网站翻了一些资料&#xff0c;要么就是方法已经被弃用&#xff0c;要么就是兼容性特别差&#xff0c;要么就是不能复制成…

HTML做一个简单漂亮的旅游网页(纯html代码)重庆旅游 7页

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 家游景点介绍 | 旅游风景区 | 家乡介绍 | 等网站的设计与制作 | HTML期末大学生网页设计作业 HTML&#xff1a;结构 CSS&#xff1a;样式 在操作方面…

HTML+CSS简单漫画网页设计成品--(红猪(9页)带注释)

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 网页设计作业 | 动漫网页设计 | 动漫网页设计成品 | 动漫网页设计成品模板 | 简单漫画网页设计成品 | HTML期末大学生网页设计作业&#xff0c;Web大学…

Linux自建RustDesk中继服务器

向日葵、ToDesk&#xff0c;想控制手机。【收费】、【收费】、【收费】、【收费】 作为编程人员&#xff0c;这钱我有点不想花。手里有常开机电脑&#xff0c;于是我萌生想法&#xff0c;使用frp做代理&#xff0c;用adb命令将手机的屏幕截图后展示在网页上&#xff0c;按秒刷…

布谷鸟搜索算法的改进及其在优化问题中的应用(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…