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

【Linux】从零开始认识五种IO模型 --- 理解五种IO模型,开始使用非阻塞IO

在这里插入图片描述

恐惧让你沦为囚犯,
希望让你重获自由。
--- 《肖申克的救赎》---

五种IO模型与阻塞IO

  • 1 前言
  • 2 五种IO模型
  • 3 非阻塞IO

1 前言

通过网络通信的学习,我们能够理解网络通信的本质是进程间通信,而进程间通信的本质就是IO。

IO就是input与output,站在进程角度,IO就是将数据从进程间输入输出数据;站在软件层面,IO就是与操作系统进行交互!以IO中常用的接口:read为例,当底层没有数据时,就会阻塞 。这种阻塞的本质是等待事件就绪。write写入数据时,会将数据拷贝到缓冲区中,当缓冲区满了之后,也会进行阻塞等待。

所以我们平时使用的IO都可以总结为等 + 拷贝!所以什么叫做高效的IO呢?IO中可以理解为等 + 拷贝,拷贝的效率是很快的,那么高效的IO就是"等"的效率高!如果调用read,write不需要等待,那么效率自然而然的就高了!

针对这个高效的IO,我们介绍一下五种IO模型

2 五种IO模型

五种IO模型是程序员们经过长时间的使用总结出来的常用情况。接下来我们使用钓鱼的例子来介绍这五种IO模型

  1. 张三今天去钓鱼,他带着一根鱼竿,鱼漂,小马扎。走到河边扎好座椅,甩杆钓鱼。张三目不转睛的盯着鱼漂,一有动静就收杆,其他事情一概不考虑,只盯着鱼漂看!
  2. 李四今天也去钓鱼,他也带着一根鱼竿,鱼漂,小马扎。他也扎好座椅,甩杆钓鱼。李四布置好之后,不时刷手机,聊天,看书。间断性的看一看检测鱼漂有没有动,鱼漂动了就需要收杆。
  3. 王五今天也来钓鱼,他是个聪明人,他布置好杆子后拿出来一个神器:铃铛,放在鱼竿上。之后就开始耍手机,看书,聊天,一有鱼上钩,铃铛就会响,就可以进行收杆!
  4. 赵六也来钓鱼了,他是附近一带的首富,带着一车鱼竿来钓鱼了,放好100根鱼竿后,赵六就开始巡逻式的检查鱼竿情况。有鱼的鱼竿就进行收杆。
  5. 田七是整个城镇的首富,他带着管家来钓鱼。田七知道自己想要的是河流里鱼,而不是钓鱼。所以他就安排司机钓鱼,他回家等待鱼,小王怎么钓鱼,田七并不关心。

上面五个人就是经典的五种IO模型,每个人都代表一种系统调用,鱼竿就是文件描述符,鱼就是数据,河是操作系统内部,鱼漂浮动就代表数据就绪,收杆就代表进行拷贝:

  • 张三的方式称之为阻塞IO
  • 李四的方式称之为非阻塞IO
  • 王五的方式称之为信号驱动IO
  • 赵六的方式称之为多路复用/多路转接IO
  • 田七的方式叫做异步IO

阻塞IO和非阻塞IO的区别就是等待的方式不同,拷贝数据时一模一样的!上面五种钓鱼效率最高的是赵六:多路复用IO,毕竟人家鱼竿多,成功等到数据的概率就高!

而非阻塞IO的高效是与阻塞IO进行对比的,张三李四一天钓的鱼最终可能差不多,但李四看完一本书,追了4集电视剧…非阻塞IO的高效体现在可以在等待IO的同时处理其他事情!

异步IO就是两件事情互不影响,等待数据与获取数据就是异步进行!同步IO与异步IO的区别就是是否自身参与IO。

阻塞 IO 是最常见的 IO 模型:

  • 阻塞 IO:在内核将数据准备好之前,系统调用会一直等待。所有的套接字,默认都是阻塞方式.
    在这里插入图片描述

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

在这里插入图片描述

  • 信号驱动 IO: 内核将数据准备好的时候,使用 SIGIO 信号通知应用程序进行 IO操作
    在这里插入图片描述

  • IO 多路转接:虽然从流程图上看起来和阻塞 IO 类似。实际上最核心在于 IO 多路转接能够同时等待多个文件描述符的就绪状态。select就是一个专门用来的接口!
    在这里插入图片描述

  • 异步 IO: 由内核在数据拷贝完成时, 通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)
    在这里插入图片描述

任何 IO 过程中, 都包含两个步骤: 第一是等待,第二是拷贝。 而且在实际的应用场景中, 等待消耗的时间往往都远远高于拷贝的时间。让 IO 更高效, 最核心的办法就是让等待的时间尽量少
这里强调两组概念:

同步通信 vs 异步通信(synchronous communication / asynchronouscommunication)同步和异步关注的是消息通信机制。

  • 所谓同步, 就是在发出一个调用时, 在没有得到结果之前, 该调用就不返回.但是一旦调用返回, 就得到返回值了;换句话说, 就是由调用者主动等待这个调用的结果
  • 异步则是相反, 调用在发出之后, 这个调用就直接返回了, 所以没有返回结果;换句话说, 当一个异步过程调用发出后, 调用者不会立刻得到结果;而是在调用发出后, 被调用者通过状态,通知来通知调用者, 或通过回调函数处理这个调用。另外,我们回忆在讲多进程多线程的时候,也提到同步和互斥。这里的同步通信和进程之间的同步是完全不相干的概念。
  • 进程/线程同步也是进程/线程之间直接的制约关系, 是为完成某种任务而建立的两个或多个线程, 这个线程需要在某些位置上协调他们的工作次序而等待、 传递信息所产生的制约关系. 尤其是在访问临界资源的时候。

以后在看到 “同步” 这个词, 一定要先搞清楚大背景是什么。 这个同步, 是同步通信异步通信的同步, 还是线程同步与互斥的同步.

阻塞 vs 非阻塞

  • 阻塞和非阻塞关注的是程序在等待调用结果(消息, 返回值) 时的状态。
  • 阻塞调用是指调用结果返回之前, 当前线程会被挂起。调用线程只有在得到结果之后才会返回。
  • 非阻塞调用指在不能立刻得到结果之前, 该调用不会阻塞当前线程

3 非阻塞IO

实现非阻塞IO的方式有很多种:
当使用open打开一个文件时,可以传入一个标志位O_NONBLOCK or O_NDELAY

 int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);

这样读取时就是非阻塞的进行读取。

同样的recv和send系列接口也有对应的非阻塞标志位MSG_DONTWAIT

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

但是这些方式都不太通用,我们可以通过fcntl接口将文件描述符设置为非阻塞的文件描述符:

Linux Programmer's Manual                                                               
FCNTL(2)NAMEfcntl - manipulate file descriptorSYNOPSIS#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );

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).

通过这个系统调用,可以写一个demo来看看效果。
这是j经典的阻塞IO:

#include <iostream>
#include <unistd.h>
#include<cstdio>
// 阻塞IO
int main()
{char buffer[1024];// 读取标准输入中的数据while (true){printf("Enter#");fflush(stdout);int n = read(0, buffer, sizeof(buffer) - 1);if (n == 0){// 说明没有读取到continue;}else if(n > 0){//读取到了数据buffer[n] = 0;std::cout << buffer ;}else{//读取出现错误了perror("error!\n");}}return 0;
}

读取效果是这样的:
在这里插入图片描述

如果没有数据输入就会阻塞等待数据输入。
那么如何更改为非阻塞IO呢?我们设置一个接口函数

#pragma once
#include <unistd.h>
#include <fcntl.h>
#include <iostream>//int fcntl(int fd, int cmd, ... /* arg */);void SetNonBlock(int fd)
{//首先获取原来标志位int fl = ::fcntl(fd , F_GETFL);if(fl < 0){std::cout<< "fcntl error " << std::endl;return ;} //设置非阻塞标志位int n = ::fcntl(fd , F_SETFL , fl | O_NONBLOCK);if(n < 0){perror("fcntl error \n");return ;}return ;
}

通过这个接口可以快速将文件描述符设置为非阻塞。我们将标准输入设置为非阻塞我们再来运行一下:
在这里插入图片描述

  • 如果是非阻塞 , 底层数据没有就绪,IO 接口会以出错形式返回。

那么如何区分是真的出错了还是底层不就绪的非阻塞IO返回呢?仅仅通过返回值是无法区分的了,但是read接口中的返回值是这么描述的:

RETURN VALUEOn success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number.  It is not an error if this number issmaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close  to  end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.  See also NOTES.On error, -1 is returned, and errno is set appropriately.  In this case, it is left unspecified whether the file position (if any) changes.

出错时,read接口会设置全局的errno!同样的recv,send…IO系列接口都是会设置errno:
在这里插入图片描述
可以看到errno被设置为了11,那11代表什么呢?11就是EWOULDBLOCK错误:

#define	EAGAIN		11	/* Try again */
...
#define	EWOULDBLOCK	EAGAIN	/* Operation would block */

这就表示底层数据不就绪,可以try again,如果真的出错了,就会被设置成其他格式。

// 非阻塞IO
int main()
{char buffer[1024];SetNonBlock(0);// 读取标准输入中的数据while (true){printf("Enter#");fflush(stdout);ssize_t n = read(0, buffer, sizeof(buffer) - 1);if (n == 0){printf("read done\n");break;}else if (n > 0){// 读取到了数据buffer[n] = 0;printf("echo# %s"  , buffer);}else{// 读取出现错误了if(errno == EWOULDBLOCK){std::cout << "底层数据没有就绪,开始轮询检测" <<std::endl;//do other thing//可以做其他事情!continue;}else{//真的出错了!perror("read error!\n");break;}}//sleep(1);}return 0;
}

这样就可以进行正常的非阻塞轮询了!
在这里插入图片描述

注意:操作系统的两个缓冲区:输入与输出,在我们键盘进行输入时会到操作系统的输入缓冲区,然后再到进程的输入缓冲区。而键盘输入时,操作系统会判断是否需要回显,回显就会将输入缓冲区的数据拷贝到输出缓冲区一份,这里就是可以回显的原因。

当我们进行IO拷贝时,如果突然接收到一个信号,导致IO拷贝中断了,那么这个读取的返回可能并没有读取完毕!这种情况的错误码是EINTR,我们可以进行判断!

相关文章:

【Linux】从零开始认识五种IO模型 --- 理解五种IO模型,开始使用非阻塞IO

恐惧让你沦为囚犯&#xff0c; 希望让你重获自由。 --- 《肖申克的救赎》--- 五种IO模型与阻塞IO 1 前言2 五种IO模型3 非阻塞IO 1 前言 通过网络通信的学习&#xff0c;我们能够理解网络通信的本质是进程间通信&#xff0c;而进程间通信的本质就是IO。 IO就是input与outp…...

Spring Boot 集成阿里云直播点播

在当今数字化时代&#xff0c;视频直播和点播服务已经成为许多应用的核心功能。阿里云提供了强大的直播和点播服务&#xff0c;能够满足各种规模的应用需求。而 Spring Boot 作为一种流行的 Java 开发框架&#xff0c;能够快速构建高效的应用程序。本文将详细介绍如何在 Spring…...

舍伍德业务安全架构(Sherwood Applied Business Security Architecture, SABSA)

舍伍德业务安全架构&#xff08;Sherwood Applied Business Security Architecture, SABSA&#xff09;是一个企业级的安全架构框架&#xff0c;它提供了一个全面的方法来设计和实现信息安全策略。SABSA模型将业务需求与安全控制相结合&#xff0c;确保企业的信息安全措施能够支…...

论可以对抗ai编程的软件开发平台(直接把软件需求描述变成软件的抗ai开发平台)的设计

论可以对抗ai编程的软件开发平台&#xff08;直接把软件需求描述变成软件的抗ai开发平台&#xff09;的设计 大家知道&#xff0c;传统的数学密码&#xff0c;都可以被量子计算机破解&#xff0c;但是这些年发展出很多数学密码&#xff0c;量子计算机也破解不了&#xff0c;叫…...

饿了么数据库表设计

有商家表、商品表、商品规格表、购物车表&#xff0c;不难分析出表是不够全面的。 (1)首先分析需要补充的表 1.对于购物车而言肯定有对应的用户&#xff0c;因此要添加一个用户表。 2.商品规格是冷&#xff0c;热&#xff0c;半分糖、全糖&#xff0c;对于冷热和半分糖是可以分…...

Flink处理乱序的数据的最佳实践

目录 网络延迟和分布式系统 事件时间与处理时间的差异 事件时间和水位线(Watermark) 时间窗口(TimeWindow) 滚动窗口(Tumbling Window) 滑动窗口(Sliding Window) 会话窗口(Session Window) 自定义Watermark生成策略 设置允许延迟和侧输出 设置允许的最大延迟时间 使…...

Android OpenGL ES详解——模板Stencil

目录 一、概念 1、模板测试 2、模板缓冲 二、模板测试如何使用 1、开启和关闭模板测试 2、开启/禁止模板缓冲区写入 3、模板测试策略函数 4、更新模板缓冲 5、模板测试应用——物体轮廓 三、模板缓冲如何使用 1、创建模板缓冲 2、使用模板缓冲 3、模板缓冲应用——…...

vscode在cmake config中不知道怎么选一个工具包?select a kit

vscode在cmake config中不知道怎么选一个工具包&#xff0c;或者发现一直在用VS的工具包想换成自己的工具包。select a kit vscode在cmake config中不知道怎么选一个工具包&#xff0c;或者发现一直在用VS的工具包想换成自己的工具包。select a kit 1.在VSCode中 按ctrlshift…...

无人机之无线电监测设备技术篇

一、技术原理 无人机的无线电监测设备主要通过捕捉和分析无人机发出的无线电信号来实现对无人机的监测和定位。这些信号包括无人机的上行遥控信号、下行数据图传信号等。设备采用多种技术手段&#xff0c;如频谱分析、信号解调、定位算法等&#xff0c;对接收到的信号进行处理和…...

【系统架构设计师】预测试卷一:案例分析

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 试题一(共25分)【问题 1】(12分)【问题 2】(13分)试题二(共 25分)【问题 1】(12分)【问题 2】(7分)【问题 3】(6分)试题三(共25分)【问题 1】(9分)【问题 2】(16分)试题四(共25分)【问题 1】…...

一篇文章教会你I2C通信(软件I2C和硬件I2C)以读取MPU6050为例,附STM32代码示例

目录 一、I2C通信介绍&#xff1a; &#xff08;1&#xff09;基本概念&#xff1a; &#xff08;2&#xff09;特点&#xff1a; &#xff08;3&#xff09;工作原理&#xff1a; 二、I2C通信原理&#xff1a; &#xff08;1&#xff09;I2C 物理层&#xff1a; &…...

Python实现SPFA算法

目录 Python实现SPFA算法引言一、SPFA算法的理论基础1.1 最短路径问题1.2 SPFA算法的基本原理1.3 SPFA算法的复杂度 二、SPFA算法的Python实现2.1 基本实现2.2 案例一&#xff1a;使用SPFA算法进行城市交通最短路径计算2.2.1 实现代码 2.3 案例二&#xff1a;负权重边的处理2.3…...

MYSQL安装(ubuntu系统)

rpm -qa 查询安装软件包 ps axj 查询服务 卸载mysql&#xff08;万不得已&#xff09; ps axj | grep mysql 查看是否存在mysql服务 systemctl stop mysqld 关闭该服务 rpm -qa | grep mysql 查安装mysql安装包 rmp -qa | grep mysql | xargs (yum apt) -y remove进行批量…...

Cpp二叉搜索树的讲解与实现(21)

文章目录 前言一、二叉搜索树的概念定义特点 二、二叉树的实现基本框架查找插入删除当只有0 ~ 1个孩子的时候当有2个孩子的时候 三、二叉树的应用K模型KV模型 四、二叉树的性能分析总结 前言 这是全新的一个篇章呢&#xff0c;二叉搜索树是我们接下来学习set、map的前提 迈过它…...

微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)

微服务设计模式 — 补偿事务模式&#xff08;Compensating Transaction Pattern&#xff09; 定义 在云计算和分布式系统中&#xff0c;管理跨多个微服务或组件的事务一致性是一项极具挑战性的任务&#xff0c;补偿事务模式Compensating Transaction Pattern&#xff09;是一种…...

20 实战:形状编码、运动补偿和纹理编码的实现(基于python)

在当今多媒体时代,视频处理与编码已经成为各个领域中不可或缺的一部分。无论是视频编辑、流媒体传输,还是计算机视觉应用,视频编码技术都扮演着关键角色。本文将详细解析一个基于Python的图形用户界面(GUI)视频编码器。通过对代码的逐行讲解、功能分析以及参数调节方法的探…...

区块链-C++挖矿软件XMRIG源码分析

C++挖矿软件源码分析 3rdpartybackendgrgon2Obfusheader.hmain 程序 xmrig.cppxmrig命名空间process类Entry::IdApp类CoreControllerbasetoolkernelinterfacesDonateStrategy.cppdonate.h/2/dmiCmake 跨平台的自动化构建系统CMakeLists.txt.cmake 13个引入算力哈希率 HashrateE…...

C语言指针的介绍

零.导言 在日常生活中&#xff0c;我们常常在外出时居住酒店&#xff0c;细心的你一定能发现酒店不同的房间上有着不同的门牌号&#xff0c;上面写着像308&#xff0c;512之类的数字。当你定了酒店之后&#xff0c;你就会拿到一个写有门牌号的钥匙&#xff0c;凭着钥匙就能进入…...

八大排序算法——堆排序

目录 前言 一、向上调整算法建堆 二、向下调整算法建堆 三、堆排序 前言 堆排序是基于堆结构的一种排序思想&#xff0c;因此要为一个乱序的数组进行排序的前提是数组必须要是一个堆&#xff0c;所以要先对数组进行建堆操作 一、向上调整算法建堆 时间复杂度&#xff1a;O…...

U盘文件不翼而飞?这些数据恢复工具帮你找回!

U盘因其便携性是我们日常工作和生活中不可或缺的工具。不过有时候它也会出点小状况。如果你U盘里的数据突然不见了&#xff0c;不要着急&#xff0c;可以先试试这几款数据恢复工具&#xff01; 福昕数据恢复 直达链接&#xff1a;www.pdf365.cn/foxit-restore/ 操作教程&…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...