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

Linux - 五种常见I/O模型

I/O操作 (输入/输出操作, Input/Output) 是指计算机与外部设备就行数据交互的过程.

什么是外部设备: 如键盘, 鼠标, 硬盘, 网卡等.

五种常见的 I/O 模型:

  1. 阻塞 I/O
  2. 非阻塞 I/O
  3. 信号驱动 I/O
  4. I/O 多路复用
  5. 异步 I/O

阻塞 I/O

阻塞 I/O 的特点: 当用户发起 I/O 请求后, 进程/线程就会被阻塞, 直到这个 I/O 操作完成.

1. 发起 I/O 请求 ( read/write ).

2. 如果数据为准备好, 那么进程/线程就会被阻塞, 进入等待状态

3. 数据准备好了, 操作系统将数据复制到用户空间, 进程/线程获取到了数据, 就被唤醒继续执行

就像是一个人去钓鱼, 当他甩鱼竿之后, 就一直盯着鱼竿什么都不做, 直到看见有鱼上钩了, 将鱼竿收起来.

阻塞 I/O: 实现简单, 容易理解.

缺点: 但是效率低下, 因为进程/线程会一直等待 I/O 操作完成, 等待期间不会执行其他的任务, 资源的利用率低.

非阻塞 I/O

上面了解了阻塞 I/O 是一直等待 I/O 操作完成形成阻塞.
那么非阻塞 I/O 也就是当进程/线程发起 I/O 请求后, 即使数据没有准备好, 也会立即返回. 不会被阻塞住.

1. 进程/线程发起非阻塞 I/O

2. 如果数据未准备好, 操作系统就会返回一个错误

3. 进程或线程需要不断的轮询, 检查数据是否准备好

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>#define BUFFER_SIZE 1024int main() {char buffer[BUFFER_SIZE];int flags;// 获取标准输入的文件描述符的当前标志flags = fcntl(STDIN_FILENO, F_GETFL, 0);if (flags == -1) {perror("fcntl F_GETFL");return 1;}// 设置标准输入为非阻塞模式if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {perror("fcntl F_SETFL");return 1;}while (1) {// 尝试从标准输入读取数据ssize_t n = read(STDIN_FILENO, buffer, BUFFER_SIZE - 1);if (n == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {// 没有数据可读,进行其他操作printf("No input available, doing other work...\n");sleep(1);} else {perror("read");break;}} else if (n > 0) {// 读取到数据,处理数据buffer[n] = '\0';printf("Read input: %s", buffer);} else {break;}        }return 0;
}

可以看到代码中对于 read 函数的操作需要使用循环不断的检查数据是否准备完成. 这个不断循环的过程就称为轮询.

就像去钓鱼, 我不会一直在那盯着鱼竿, 而是每过一会就去看看有没有鱼上钩, 在这期间可以去看看手机, 刷刷视频.

优点: 如果检测到数据还没准备好, 那么此时程序也可以执行一些耗时不长的任务, 这也是非阻塞 I/O 的一个优点.

缺点: 轮询机制会占用大量的 CPU 资源, 效率低下.

信号驱动 I/O

基于信号通知进程/线程 I/O 操作完成.

1. 程序注册一个信号处理器

2. 当数据准备好了, 操作系统就会发送对应的信号通知程序

3. 程序接收到信号后, 信号处理器就会执行对应操作

钓鱼的时候, 每过一段时间就去看看太麻烦了, 所以在鱼竿上装了一个铃铛, 当有鱼咬钩时, 铃铛就会响. 铃铛响将相当于是信号. 铃铛不响我就继续做我自己的事, 看看手机....

优点: 这也是非阻塞的一个 I/O 模型, 有着非阻塞 I/O 的优点. 通过信号可以准确快速的响应 I/O 事件.

缺点: 那么引入了信号, 程序就必然会变得更加复杂, 容易出现错误.

I/O 多路复用

上面的 I/O 模型中, 都是对某一个 I/O 操作进行管理. 

I/O 多路复用则是对于多个 I/O 操作进行管理, 当某个 文件描述符(fd) 准备好了后, 系统就会发送通知, 此时程序就可以对这些 准备好了的 fd 进行操作

1. 通过 select, poll 或 epoll 监控多个文件描述符 (fd) 的状态

2. 当有 fd 准备好了后, 操作系统通知程序, 程序就对这些准备好的 fd 进行操作

上面钓鱼中, 我只带了一根钓鱼竿 , 那么现在我带了50根钓鱼竿, 50根杆同时钓鱼, 效率大幅提升.

异步 I/O

前面的四种都是同步 I/O. I/O = 等待 + 拷贝. 同步 I/O 至少参与了一个过程 (等待或拷贝).

而异步 I/O 则是两个都不参加. 有操作系统完成 I/O 操作, 操作系统完成后通知进程/线程. 进程/线程可以通过回调函数或事件通知机制获取本次 I/O 操作的结果.

1. 用户进程/线程发起异步 I/O 请求, 直接返回

2. 操作系统负责完成 I/O 操作, 并在完成后通知用户进程/线程

3. 用户线程通过回调函数或事件通知机制获取本次 I/O 结果

之前钓鱼, 都是自己在操作, 但是现在我雇佣一个人来帮我钓鱼, 当他钓鱼结束后, 将钓到的鱼交给我就行, 我不用去管钓鱼的事.

当然这一种模型也很复杂的. 对于程序员的要求较高

同步和异步

同步: 必须等待任务完成后, 才能执行下一个任务. 像上面的四种 I/O 模型中, 无论是阻塞还是非阻塞, 我们都在等待它的返回值 (等待它执行的结果). 只有等到了它执行的结果, 程序才会继续向下执行. 异步也是不断地继续轮询, 直到等到执行的结果

异步: 则不会等待这个任务, 而是继续向后执行. 如异步的进行 I/O 请求, 虽然也会直接进行返回, 但是这个返回并没有附带本次操作的结果. 重点并不是返回, 重点是本次请求的结果是否被等待了. 至于本次任务执行的结果, 则是通过其他方法通知给程序 (如: 回调函数等)

http://www.lryc.cn/news/525362.html

相关文章:

  • 【负载均衡式在线OJ】加载题目信息(文件版)
  • “上门按摩” 小程序开发项目:基于 SOP 的全流程管理
  • WPF1-从最简单的xaml开始
  • 2025牛客寒假算法营2
  • 编译Android平台使用的FFmpeg库
  • 【C++高并发服务器WebServer】-2:exec函数簇、进程控制
  • 力扣707题(2)——设计链表
  • K8S中ingress详解
  • SpringBoot打包为JAR包或WAR 包,这两种打包方式在运行时端口将如何采用?又有什么不同?这篇文章将给你解惑
  • zabbix6.0安装及常用监控配置
  • SQL-leetcode—1179. 重新格式化部门表
  • JavaWeb 学习笔记 XML 和 Json 篇 | 020
  • 在Raspbian上,如何获取树莓派的CPU当前频率
  • 网络打印机的搜索与连接(一)
  • LangChain + llamaFactory + Qwen2-7b-VL 构建本地RAG问答系统
  • 【自然语言处理(NLP)】介绍、发展史
  • 1.CSS的三大特性
  • 【分布式日志篇】从工具选型到实战部署:全面解析日志采集与管理路径
  • 基于springcloud汽车信息分析与可视化系统
  • TOGAF之架构标准规范-信息系统架构 | 数据架构
  • Databend x 沉浸式翻译 | 基于 Databend Cloud 构建高效低成本的业务数据分析体系
  • cuda的并行运算介绍
  • 「全网最细 + 实战源码案例」设计模式——抽象工厂模式
  • 领域驱动设计(DDD)四 订单管理系统实践步骤
  • leetcode 面试经典 150 题:简化路径
  • 基于 STM32 的智能农业温室控制系统设计
  • 【Spring Boot】掌握 Spring 事务:隔离级别与传播机制解读与应用
  • 【Postgres_Python】使用python脚本将多个PG数据库合并为一个PG数据库
  • Tailwind CSS v4.0 发布
  • pandas基础:文件的读取和写入