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

五种 IO 模式的简单介绍 -- 阻塞 IO,非阻塞 IO,信号驱动 IO,IO 多路复用,异步 IO

目录

1. 同步阻塞 IO(Blocking IO)

2. 同步非阻塞 IO(Non-Blocking IO)

2.1 同屋非阻塞 IO 介绍

2.2 错误码介绍

2.3 非阻塞 IO demo 代码

3. 信号驱动 IO(Signal Driven IO,SIGIO)

4. IO 多路复用(IO Multiplexing) 

5. 异步 IO(Asynchronous IO,AIO)


        IO 的过程可以分为等待数据和拷贝数据两个阶段

1. 同步阻塞 IO(Blocking IO)

        同步阻塞 IO:进程发起 IO 操作后会被阻塞,知道 IO 操作完成才返回,期间无法处理其他任务。阻塞 IO 是最常见的 IO 模型。

        如上图所示,进程调用 recvfrom 向内核发送读请求(读取文件、网络接收数据),内核等待数据就绪(数据从磁盘加载到内存、网络数据到达),数据就绪后,内核将数据拷贝到进程(用户)缓冲区,进程解除阻塞并处理数据。

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>int main()
{char buffer[1024];while(true){std::cout << "请输入数据: ";fflush(stdout); // 立即将内核缓冲区的数据刷新到标准输出中ssize_t n = read(0, buffer, sizeof(buffer));if (n > 0){buffer[n - 1] = 0;std::cout << "读取的数据:" << buffer << std::endl;}elsebreak;}r

        如上图,标准输入默认情况下是阻塞模式的,所以每次都会等待用户进行输入之后才进行数据处理。 

2. 同步非阻塞 IO(Non-Blocking IO)

2.1 同屋非阻塞 IO 介绍

        同步非阻塞 IO:进程发起 IO 请求后立即返回,通过轮询方式查询 IO 操作是否完成,不会阻塞进程

        非阻塞 IO 往往需要轮询的方式反复尝试读写文件描述符,这对 CPU 来说是较大的浪费。

        如上图,进程调用 recvfrom 发起读请求,第一次内核中无数据就绪,则返回,然后通过循环调用 recvfrom 发起第二次读请求,内核中依旧无数据就绪,继续返回,知道有数据就绪后,内核将数据拷贝到进程缓冲区中,并进行处理。 

        fcntl 函数:fcntl(file control)是 Linux 系统中用于操作文件描述符属性的核心系统调用,提供了对文件、套接字、管道等 IO 资源的高级控制能力。

#include <unistd.h>
#include <fcntl.h>int fcntl(int fd, int cmd, ...);

        fd:目标文件描述符。

        cmd:操作命令,决定 fcntl 的具体功能。

        ...:可变参数,根据 cmd 不同可能为整数、指针或结构体。        

        返回值:成功时根据 cmd 返回不同值,失败返回 -1, 并设置 errno。 

        下列介绍部分 cmd 参数:

        (1)F_GETFL:返回 fd 的状态标志(如 O_EDONLY、O_NONBLOCK)

        (2)F_SETFL:修改 fd 状态标志,常用标志包括:
                O_NONBLOCK:设置为非阻塞 IO

                O_APPEND:设置为追加模式。

                O_DIRECT:绕过内核缓冲区,直接 IO。

                O_ASYNC:启动信号驱动 IO 通知。

2.2 错误码介绍

        read 函数用于从文件描述符中读取数据,返回读取的字节数,错误时返回 -1,并设置 errno(错误码)。

       下列介绍部分调用 read 函数时发生错误设置的错误码。

       (1)EAGAIN:表示操作无法立即完成,需要重试(通常用于文件描述符设置为非阻塞时)。

       (2)EWOULDBLOCK:与 EAGAIN 本质相同,是其别名,不同系统可能定义不同,在 POSXI 标准中,EWOULDBLOCK 常用于套接字操作。

        (3)EINTR:表示阻塞的系统调用被信号(如 SIGINT、SIGTERM)中断,导致调用未完成而返回错误。

2.3 非阻塞 IO demo 代码

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>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()
{SetNonBlock(0);  // 设置标准输入为非阻塞char buffer[1024];while(true){ssize_t n = read(0, buffer, sizeof(buffer));if (n > 0){buffer[n - 1] = 0;std::cout << buffer << std::endl;sleep(1);}else if (n < 0) // 底层数据没有准备好,数据读取不算出错{// 1. 读取出错或者数据没有就绪if (errno == EAGAIN || errno == EWOULDBLOCK)    // 错误码{std::cout << "数据没有就绪... " << std::endl;sleep(1);// 其他业务// ....continue;}else if (errno == EINTR)continue;else    // 其他错误break;}elsebreak;}return 0;
}

3. 信号驱动 IO(Signal Driven IO,SIGIO)

        信号驱动 IO:进程通过注册信号处理函数,当 IO 操作就绪时,内核向进程发送信号(如 SIGIO),进程通过信号回调处理 IO 事件

        如上图,进程将 fd 设置为信号驱动模式,并绑定信号处理函数,内核等待数据就绪后,向进程发送 SIGIO 信号,进程捕获信号,在信号处理函数中完成 IO 操作。 

4. IO 多路复用(IO Multiplexing) 

        IO 多路复用:通过一个进程监控多个文件描述符,当其中一个或多个准备就绪时,进程被唤醒并处理对应的 IO 操作

        在 Linux 中有三种机制进行 IO 多路复用:select、poll、epoll。

        select:监控 fd(文件描述符)集合,通过遍历 fd 判断就绪状态fd 数量受限于 FD_SETSIZE(默认 1024)。该机制支持跨平台,但是效率低,并且监控 fd 数量有限,适合小规模连接。

        poll:用链表存储 fd,无数量限制,但仍需遍历所有 fd 进行就绪状态的判断。该机制监控的 fd 没数量限制,效率比 select 高(但是仍需遍历所以 fd,效率也不是特别高)。

        epoll:事件驱动模型,内核维护就绪 fd 列表仅通知就绪事件(LT/ET 模式)。Linux 高新能机制,支持百万级连接,适用于高并发场景(如 Nginx、Redis)。

        上述三种机制在之后进行详细介绍。

5. 异步 IO(Asynchronous IO,AIO)

        异步 IO:进程发起 IO 请求后立即返回,内核在 IO 操作完成(包括数据拷贝)后通知进程,全程无需进程主动干预

        如上图,进程通过 aio_read 向内核提交 IO 请求,指定回调函数或事件通知方式。内核负责数据读取和拷贝当用户缓冲区,完成后通过信号、回调或事件通知进程,进程处理 IO 结构,无需关注中间过程。 

        

http://www.xdnf.cn/news/14595.html

相关文章:

  • RISC-V三级流水线项目:总体概述和取指模块
  • 基于java SSM的房屋租赁系统设计和实现
  • python基于微信小程序的广西文化传承系统
  • 【入门级-基础知识与编程环境:3、计算机网络与Internet的基本概念】
  • VLN论文复现——VLFM(ICRA最佳论文)
  • AI-Sphere-Butler之如何将豆包桌面版对接到AI全能管家~新玩法(一)
  • 虚拟 DOM 与 Diff 算法:现代前端框架的核心机制
  • 边缘-云协同智能视觉系统:实时计算与云端智能的融合架构
  • PillarNet: Real-Time and High-PerformancePillar-based 3D Object Detection
  • MySQL 8.x配置MGR高可用+ProxySQL读写分离(二):ProxySQL配置MySQL代理及读写分离
  • HarmonyOS 5 多端适配原理与BreakpointSystem工具类解析:附代码
  • Flutter ListTile 徽章宽度自适应的真正原因与最佳实践
  • 十四天机器学习入门——决策树与随机森林:从零构建智慧决策模型
  • Python Django全功能框架开发秘籍
  • Jenkins部署及反向代理
  • 【JS-4.7-表单value属性】深入理解DOM操作中的表单value属性
  • 雷达高度计 RA-6500
  • AI浪潮拐点:MCP与A2A协议如何重塑AI智能体协作生态
  • 金融行业B端系统布局实战:风险管控与数据可视化的定制方案
  • 动手用 Web 实现一个 2048 游戏
  • 如何预防电磁铁损坏
  • Data Vault 初探(九) —— 定期装载_Kettle_附属表
  • Java性能优化权威指南-操作系统性能监控
  • HarmonyOS NEXT应用元服务布局优化ArkUI框架执行流程
  • 从java角度理解io多路复用和redis为什么使用io多路复用
  • PixPin:一个强大且免费的截图贴图工具
  • SpringBoot+Vue服装商城系统 附带详细运行指导视频
  • 群晖 NAS Docker 镜像加速配置详细教程
  • 开源 python 应用 开发(二)基于pyautogui、open cv 视觉识别的工具自动化
  • RSA加密原理及推导