五种IO模型

目录

一、对IO的重新认识

二、IO的五种模型

1.阻塞IO

2.非阻塞IO

3.信号驱动IO

4.IO多路转接

5.异步IO

6.一些概念的解释

三、非阻塞IO的代码实现

1.fcntl

2.实现工具类

3.实现主程序


一、对IO的重新认识

如果有人问你IO是什么,你该怎么回答呢?

你可能会说,IO不就是input和output表示输入和输出,输入表示把数据从硬盘等外设拷贝到内存,而输出表示把数据从内存拷贝到其他外设。

虽然这样说没什么大问题,但还不够深刻。

我们不妨设想下面的现象,有一个进程,调用read/recv这样的系统调用读取数据。如果此时读取条件不满足,那就没有数据可供进程读取,进程只就会一直等待数据准备好。

IO除了拷贝数据需要消耗时间,还包含这个等待的过程所以我们使用的系统调用除了拷贝代码,也包含了等的这部分代码。

也就是说,IO=等+数据拷贝

那什么是高效IO呢?

我们知道,IO过程我们在意的是拷贝,而不是等待。而拷贝需要的时间是由电路还有系统实现等保证的。随着科技的发展,拷贝本身花费的时间已经基本没有提升空间了,所以拷贝本身的效率已经很难再有提升了。那么等待时间的长度就决定了IO的效率。

换句话说,单位时间内,等待的比重越低,IO效率越高。

二、IO的五种模型

1.阻塞IO

在内核将数据准备好之前,系统调用会一直等待。我们之前写代码使用的IO接口读取文件描述符,默认都使用阻塞IO方式。

下图就是阻塞IO的示意图,进程调用recvfrom这样的IO接口从内核中读取数据。如果数据没有准备好,进程就会阻塞在调用处等待,数据准备好后,才会将内核中的数据拷贝到用户缓冲区,并给出返回值。

阻塞IO是最常见的IO模型,也最简单,我们之前写的所有代码,IO都是阻塞式的。

2.非阻塞IO

如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EAGAN或者EWOULDBLOCK错误码。

如图所示,进程调用recvfrom从内核缓冲区中读取数据。如果数据没有准备好,就会给进程返回一个EWOULDBLOCK错误码,告诉进程数据还没准备好,进程就会接着去干自己的事情。

过了一段时间,进程还会调用recvfrom读取数据,不断反复,直到数据准备好。接着系统调用完成拷贝并返回成功的返回值。

非阻塞IO需要程序员设计循环代码,反复尝试读写文件描述符,这个过程称为轮询。但轮询对CPU有一定的性能浪费,只有特定场景下才使用。

3.信号驱动IO

信号驱动IO会在内核将数据准备好的时候,发送SIGIO信号通知进程进行IO操作。

如图所示,信号驱动IO模型,该模式使用信号处理函数执行IO。

首先使用signal注册信号处理函数为包含IO系统调用的函数。所以只要进程收到信号,就可以在处理函数中调用recvfrom拷贝已经准备好的数据。

也就是说,只要数据准备完成了,进程就会收到信号,进程直接来拷贝就可以了。其余时间进程还可以继续执行自己的代码。

但是我们之前也说过,如果我们给一个进程同时发很多信号,只有两个能被最终递达。而这里的信号丢失就相当于读取次数减少,就相当于数据丢失。所以,这种很少有符合这种模式的IO状态。

4.IO多路转接

IO多路转接可以理解为多个阻塞IO同时进行,并不断遍历检测哪个IO的文件描述符准备好了,准备好了就会执行拷贝。

如图所示为IO多路转接模式,它将IO的等待和拷贝分开了。

进程调用select系统调用等待内核中的数据就绪,就绪以后会通知进程调用recvfrom来将数据拷贝到用户缓冲区中。

由于多路转接可以同时等待多个文件描述符。所以,当一个或者多个文件的缓冲区中数据就绪时,都会通知上层用户读取。而且每个拷贝过程也是并行的,还是免不了等,但是等的比重降低了很多,从而提高了IO的效率。

多路转接既是效率最高的IO模式,也是我们以后讲解的重点。

5.异步IO

当一个异步IO调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态、信号等来通知调用者,或通过回调函数处理这个调用。

下图表示异步IO,进程调用aio_read,将等待数据就绪和将数据拷贝到用户缓冲区两个步骤的工作全部交给操作系统来完成。当操作系统完成两个步骤以后,直接通知上层用户去用户缓冲区中读取数据即可。

也就是,进程不需要再考虑数据的IO,而是将其全权交给操作系统完成。

6.一些概念的解释

什么是同步IO和异步IO?

同步和异步的区别在于消息通信的机制。

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回,但是一旦调用返回,就得到返回值了。

换句话说就是,调用者在主动等待这个调用的结果。

而异步则是调用开始执行后直接返回,调用者不会立刻得到结果,而是在调用返回后,被调用者通过状态、信号等通知调用者或通过回调函数处理。

话句话说,就是把事情交给了其他应用去做,自己只根据通信数据接收处理结果。

线程同步和同步IO有什么关系?

我们在讲解Linux线程时也提到了同步。

线程同步表示多个线程同时对临界资源进行操作时,系统为了保证没有线程处于饥饿状态,会以一定的顺序安排各个线程的执行顺序。

而同步IO表示处理数据的进程本身是否全权参与IO过程。

也就是,同步IO和线程同步之间,除了都有同步这个词之外没有任何关系。

三、非阻塞IO的代码实现

1.fcntl

int fcntl(int fd, int cmd, ... /* arg */ );

头文件:unistd.h、fcntl.h

功能:修改文件描述符的属性或对其进行其他操作。

参数:int fd需要操作的文件描述符。int cmd表示对描述符的操作。...表示可变参数列表,传入的cmd不同,参数也不同

返回值:成功返回非-1的值,失败返回-1。

fcntl函数有5种功能:

  • 复制一个现有的描述符(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).

我们只使用第三个功能,即获取/设置文件状态标记,可将一个文件描述符设置为非阻塞。我们写一个SetNonBlock函数支持该功能。

//将文件描述符设为非阻塞
void SetNonBlock(int fd)
{int fl = fcntl(fd, F_GETFL);//获取文件描述符的标志,该标志是一个位图结构if(fl < 0)//获取失败{std::cerr << "fctnl:" << strerror(errno) << std::endl;//打印错误码}else{fcntl(fd, F_SETFL, fl | O_NONBLOCK);//将该文件描述符设为非阻塞}
}

2.实现工具类

util.hpp

#pragma once
#include<iostream>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>//将文件描述符设为非阻塞
void SetNonBlock(int fd)
{int fl = fcntl(fd, F_GETFL);//获取文件描述符的标志,该标志是一个位图结构if(fl < 0)//获取失败{std::cerr << "fctnl:" << strerror(errno) << std::endl;//打印错误码}else{fcntl(fd, F_SETFL, fl | O_NONBLOCK);//将该文件描述符设为非阻塞}
}void print_work()
{std::cout << "I am working. ";
}

3.实现主程序

由于我们从标准输入流(文件描述符为0)中读取数据,所以只要我们敲击键盘输入文字,就相当于向标准输入流中写入数据。

main.cc

#include"util.hpp"
#include<iostream>int main()
{SetNonBlock(0);//设置文件描述符为非阻塞while(1){char buffer[1024];ssize_t n = read(0, buffer, sizeof(buffer)-1);//读取数据if(n > 0)//读到了数据{buffer[n] = '\0';std::cout << buffer << std::endl;}else if(n == 0)//读到了结尾{std::cout << "read end" << std::endl;break;}else//n等于-1有两种情况,一种是读取出错,另一种是数据还没准备好,read只能按-1返回{if (errno == EAGAIN)//错误码为EAGAIN表示数据还没有准备好{//std::cout << "我没错, 只是没有数据" << std::endl;print_work();//程序继续执行自己的事}else if (errno == EINTR)//错误码为EINTR表示读取时进程收到了信号,需要进行处理,读取就被暂时打断了。{continue;//继续循环}else//这次就是出错了,打印错误码就可以了{std::cout << " errno: " << strerror(errno) << std::endl;break;}}sleep(1);}return 0;
}

此时即使屏幕上输出的数据乱成一团,但是并不会影响输入标准输入流的信息。

换句话说,非阻塞等待时,程序确实可以继续执行自己的流程。

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

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

相关文章

LeetCode题:83删除排序链表中的重复元素 141环形链表

83删除排序链表中的重复元素 题目内容 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2]示例 2&#xff1a; 输入&#xf…

2023-2024-1高级语言程序设计-一维数组

7-1 逆序输出数组元素的值 从键盘输入n个整数存入一维数组中&#xff0c;然后将数组元素的值逆序输出。 输入格式: 第一行输入整数个数n&#xff1b; 第二行输入n 个整数&#xff0c;数据之间以空格隔开。 输出格式: 逆序输出数组元素的值&#xff0c;每个数据之后跟一个空…

基于野狗算法的无人机航迹规划-附代码

基于野狗算法的无人机航迹规划 文章目录 基于野狗算法的无人机航迹规划1.野狗搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用野狗算法来优化无人机航迹规划。 1.野狗搜索算法 …

SpringBoot整合RabbitMQ学习笔记

SpringBoot整合RabbitMQ学习笔记 以下三种类型的消息&#xff0c;生产者和消费者需各自启动一个服务&#xff0c;模拟生产者服务发送消息&#xff0c;消费者服务监听消息&#xff0c;分布式开发。 一 Fanout类型信息 . RabbitMQ创建交换机和队列 在RabbitMQ控制台&#xff0c;新…

JS逆向爬虫---请求参数加密① 【某度翻译】

接口定位 抓包输入翻译关键词 全局搜索关键词,定位到接口https://fanyi.baidu.com/v2transapi 全局搜索sign 多次尝试定位变化参数sign 断点调试b函数 复制整个function&#xff0c;并测试函数运行结果。 需要把function改写成如下的数据&#xff1a; function(t) {var o…

reactor(百万并发服务器) - 2

这是连续剧般的文章&#xff0c;请关注&#xff0c;持续更新中... 系列文章: http://t.csdnimg.cn/Os83Qhttp://t.csdnimg.cn/Os83Q这篇文章将我们的reactro转变成http服务器... HTTP HTTP介绍 HTTP是一个基于TCP通信协议的基础上的应用层协议。接下来我们需要解析HTTP请求消息…

【2024最新】PE工具箱【下载安装】零基础到大神【附下载链接】

下载链接&#xff1a;点这里 1.PE (Portable Executable) 工具箱通常用于处理Windows可执行文件和动态链接库&#xff08;DLL&#xff09;的二进制文件格式。这些工具对于进行逆向工程、软件分析和系统维护等任务非常有用。以下是PE工具箱的一些常见功能和用法&#xff1a; 查…

VR博物馆:让博物馆传播转化为品牌影响力

随着VR技术的不断进步&#xff0c;VR全景技术已经成为了文化展示和传播的一项重要工具&#xff0c;相较于传统视频、图文等展现方式&#xff0c;VR全景体验更加直观、便捷&#xff0c;其中蕴涵的信息量也更加丰富&#xff0c;这也为公众了解博物馆和历史文化带来了更为深刻的体…

洗衣洗鞋柜洗衣洗鞋小程序

支持&#xff1a;一键投递、上门取衣、自主送店、多种支付方式 TEL: 17638103951(同V) -----------------用户下单-------------- -------------------------多种支付和投递方式------------------------- -----------------商家取鞋--------------

Transformer的最简洁pytorch实现

目录 前言 1. 数据预处理 2. 模型参数 3. Positional Encoding 4. Pad Mask 5. Subsequence Mask 6. ScaledDotProductAttention 7. MultiHeadAttention 8. FeedForward Networks 9. Encoder Layer 10. Encoder 11. Decoder Layer 12. Decoder 13. Transformer 1…

基于STM32HAL库看门狗(独立看门狗)-简述

目录 概述 一、开发环境 二、STM32CubeMx配置 三、编码 四、运行结果 五、总结 概述 一个成熟靠谱的项目&#xff0c;离不开“看门狗”的必选项&#xff0c;凡是人写的程序多少都会有出现bug的情况&#xff08;或芯片外设受外界干扰导致故障程序卡死、跑飞的情况&#xf…

AI:58-基于深度学习的猫狗图像识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

【LeetCode】每日一题 2023_11_5 重复的DNA序列

文章目录 刷题前唠嗑重复的DNA序列题目描述代码和解题思路偷看大佬题解结语 刷题前唠嗑 LeetCode? 启动&#xff01;&#xff01;&#xff01; 重复的DNA序列 题目链接&#xff1a;187. 重复的DNA序列 题目描述 代码和解题思路 func findRepeatedDnaSequences(s string) …

基于SpringBoot+Vue的旅游系统、前后端分离

博主24h在线&#xff0c;想要源码文档部署视频直接私聊&#xff0c;低价有偿&#xff01; 基于SpringBootVue的旅游系统、前后端分离 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI 工具&#xff1a;IDEA/Eci…

使用脚手架创建项目,使用组件开发

单文件组件 单文件组件就是一个文件对应一个组件, 单文件组件的名字通常是xxx.vue(命名规范和组件名的命名规范相同),这个文件是Vue框架规定的只有它能够认识&#xff0c;浏览器无法直接打开运行 Vue框架可以将xxx.vue文件进行编译为浏览器能识别的html js css的代码 xxx.vu…

蒙哥马利算法模乘(四)

一 蒙哥马利算法模乘介绍 蒙哥马利模乘算法主要为了进行大数运算a*b mod n,在介绍蒙哥马利模乘之前,先让我们来了解蒙哥马利约减。 1.1 蒙哥马利约减 a mod n 如果a是一个2048位的整数,n是一个1024位的整数,如果直接采用相除的方式,不论在空间还是时间上都会产生非常大…

Swift 和 Python 两种语言中带关联信息错误(异常)类型的比较

0. 概览 如果我们分别在平静如水、和谐感人的 Swift 和 Python 社区抛出诸如“Python 是天下最好的语言…” 和 “Swift 是宇宙第一语言…”之类的言论会有怎样的“下场”&#xff1f; 我们并不想对可能发生的“炸裂”景象做出什么预测&#xff0c;也无意比较 Swift 与 Pytho…

安吉寻梦桃花原

安吉——西湖边的那片竹海 安吉县&#xff0c;地处浙江西北部&#xff0c;湖州市辖县之一&#xff0c;北靠天目山&#xff0c;面向沪宁杭。建县于公元185年&#xff0c;县名出自《诗经》“安且吉兮”之意。 安吉县生态环境优美宜居&#xff0c;境内“七山一水二分田”&#xf…

Java锁常见面试题

图片引用自&#xff1a;不可不说的Java“锁”事 - 美团技术团队 1 java内存模型 java内存模型(JMM)是线程间通信的控制机制。JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存中&#xff0c;每个线程都有一个私有的本地内存&#xff0c;本地内存中存储了该…

AI:59-基于深度学习的行人重识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…