【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模型
- 张三今天去钓鱼,他带着一根鱼竿,鱼漂,小马扎。走到河边扎好座椅,甩杆钓鱼。张三目不转睛的盯着鱼漂,一有动静就收杆,其他事情一概不考虑,只盯着鱼漂看!
- 李四今天也去钓鱼,他也带着一根鱼竿,鱼漂,小马扎。他也扎好座椅,甩杆钓鱼。李四布置好之后,不时刷手机,聊天,看书。间断性的看一看检测鱼漂有没有动,鱼漂动了就需要收杆。
- 王五今天也来钓鱼,他是个聪明人,他布置好杆子后拿出来一个神器:铃铛,放在鱼竿上。之后就开始耍手机,看书,聊天,一有鱼上钩,铃铛就会响,就可以进行收杆!
- 赵六也来钓鱼了,他是附近一带的首富,带着一车鱼竿来钓鱼了,放好100根鱼竿后,赵六就开始巡逻式的检查鱼竿情况。有鱼的鱼竿就进行收杆。
- 田七是整个城镇的首富,他带着管家来钓鱼。田七知道自己想要的是河流里鱼,而不是钓鱼。所以他就安排司机钓鱼,他回家等待鱼,小王怎么钓鱼,田七并不关心。
上面五个人就是经典的五种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
恐惧让你沦为囚犯, 希望让你重获自由。 --- 《肖申克的救赎》--- 五种IO模型与阻塞IO 1 前言2 五种IO模型3 非阻塞IO 1 前言 通过网络通信的学习,我们能够理解网络通信的本质是进程间通信,而进程间通信的本质就是IO。 IO就是input与outp…...
Spring Boot 集成阿里云直播点播
在当今数字化时代,视频直播和点播服务已经成为许多应用的核心功能。阿里云提供了强大的直播和点播服务,能够满足各种规模的应用需求。而 Spring Boot 作为一种流行的 Java 开发框架,能够快速构建高效的应用程序。本文将详细介绍如何在 Spring…...
舍伍德业务安全架构(Sherwood Applied Business Security Architecture, SABSA)
舍伍德业务安全架构(Sherwood Applied Business Security Architecture, SABSA)是一个企业级的安全架构框架,它提供了一个全面的方法来设计和实现信息安全策略。SABSA模型将业务需求与安全控制相结合,确保企业的信息安全措施能够支…...
论可以对抗ai编程的软件开发平台(直接把软件需求描述变成软件的抗ai开发平台)的设计
论可以对抗ai编程的软件开发平台(直接把软件需求描述变成软件的抗ai开发平台)的设计 大家知道,传统的数学密码,都可以被量子计算机破解,但是这些年发展出很多数学密码,量子计算机也破解不了,叫…...
饿了么数据库表设计
有商家表、商品表、商品规格表、购物车表,不难分析出表是不够全面的。 (1)首先分析需要补充的表 1.对于购物车而言肯定有对应的用户,因此要添加一个用户表。 2.商品规格是冷,热,半分糖、全糖,对于冷热和半分糖是可以分…...
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中不知道怎么选一个工具包,或者发现一直在用VS的工具包想换成自己的工具包。select a kit vscode在cmake config中不知道怎么选一个工具包,或者发现一直在用VS的工具包想换成自己的工具包。select a kit 1.在VSCode中 按ctrlshift…...
无人机之无线电监测设备技术篇
一、技术原理 无人机的无线电监测设备主要通过捕捉和分析无人机发出的无线电信号来实现对无人机的监测和定位。这些信号包括无人机的上行遥控信号、下行数据图传信号等。设备采用多种技术手段,如频谱分析、信号解调、定位算法等,对接收到的信号进行处理和…...
【系统架构设计师】预测试卷一:案例分析
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 试题一(共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通信介绍: (1)基本概念: (2)特点: (3)工作原理: 二、I2C通信原理: (1)I2C 物理层: &…...
Python实现SPFA算法
目录 Python实现SPFA算法引言一、SPFA算法的理论基础1.1 最短路径问题1.2 SPFA算法的基本原理1.3 SPFA算法的复杂度 二、SPFA算法的Python实现2.1 基本实现2.2 案例一:使用SPFA算法进行城市交通最短路径计算2.2.1 实现代码 2.3 案例二:负权重边的处理2.3…...
MYSQL安装(ubuntu系统)
rpm -qa 查询安装软件包 ps axj 查询服务 卸载mysql(万不得已) 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模型 四、二叉树的性能分析总结 前言 这是全新的一个篇章呢,二叉搜索树是我们接下来学习set、map的前提 迈过它…...
微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)
微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern) 定义 在云计算和分布式系统中,管理跨多个微服务或组件的事务一致性是一项极具挑战性的任务,补偿事务模式Compensating Transaction Pattern)是一种…...
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语言指针的介绍
零.导言 在日常生活中,我们常常在外出时居住酒店,细心的你一定能发现酒店不同的房间上有着不同的门牌号,上面写着像308,512之类的数字。当你定了酒店之后,你就会拿到一个写有门牌号的钥匙,凭着钥匙就能进入…...
八大排序算法——堆排序
目录 前言 一、向上调整算法建堆 二、向下调整算法建堆 三、堆排序 前言 堆排序是基于堆结构的一种排序思想,因此要为一个乱序的数组进行排序的前提是数组必须要是一个堆,所以要先对数组进行建堆操作 一、向上调整算法建堆 时间复杂度:O…...
U盘文件不翼而飞?这些数据恢复工具帮你找回!
U盘因其便携性是我们日常工作和生活中不可或缺的工具。不过有时候它也会出点小状况。如果你U盘里的数据突然不见了,不要着急,可以先试试这几款数据恢复工具! 福昕数据恢复 直达链接:www.pdf365.cn/foxit-restore/ 操作教程&…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
