【Linux笔记】IO模型

一、IO理解

1.1 什么是 IO

IO(Input/Output)指的是:计算机系统中数据的输入与输出操作。

在 Linux 中 "一切皆文件",网络套接字、磁盘文件、管道、终端设备等,都对应一个文件描述符(fd)

内核通过 fd 来统一管理所有 IO 资源。

1.2 IO 操作的两个阶段

一次 IO 读操作分为两个阶段:

复制代码
阶段1:等待数据就绪
  - 数据从网卡/磁盘到达内核缓冲区(kernel buffer)
  - 此时进程可能需要等待

阶段2:数据拷贝
  - 内核将数据从内核缓冲区拷贝到用户进程缓冲区(user buffer)
  - 拷贝完成后返回

二、五种 IO 模型

2.1 阻塞 IO

复制代码
用户进程                        内核

   |                             |
   |-- read(fd, buf) ----------->|
   |       (进程阻塞)             |-- 等待数据到达
   |                             |-- 数据到达内核buffer
   |                             |-- 内核拷贝数据到用户buf
   |<------- 返回 --------------- |
   |                             |
  • 所有的 socket 默认都是阻塞模式

  • 在数据未就绪时,进程被挂起,无法做其他事

  • 两个阶段都会阻塞

2.2 非阻塞 IO ★

复制代码
用户进程                        内核

   |                             |
   |-- read(fd, buf) ----------->|
   |<------- EAGAIN ------------ |  (数据未就绪)
   |                             |
   |  (做其他事或继续轮询)          |
   |                             |
   |-- read(fd, buf) ----------->|
   |<------- 返回数据 ------------|  (数据已就绪)
  • 通过 fcntl 设置 O_NONBLOCK 标志

  • 数据未就绪时立即返回 EAGAIN/EWOULDBLOCK

  • 需要不断轮询,浪费 CPU

2.3 信号驱动 IO

复制代码
用户进程                        内核

   |                             |
   |-- 注册SIGIO信号处理函数 ---->  |
   |-- fcntl设置属主和信号 ------->|
   |       (做其他事)             |
   |                             |-- 数据到达
   |<------ SIGIO信号 ----------- |
   |-- read(fd, buf) ----------->|
   |<------- 返回数据 ----------- |
  • 内核在数据就绪时发送 SIGIO 信号通知进程

  • 进程收到信号后再调用 read 读取

  • 实际使用较少

2.4 IO 多路复用 ★

复制代码
用户进程                        内核

   |                             |
   |-- select/poll/epoll_wait -->|
   |       (阻塞在select上)       |-- 监控多个fd
   |                             |-- 某个fd就绪
   |<------- 返回就绪fd列表 -----  |
   |                             |
   |-- read(就绪的fd) ----------> |
   |<------- 返回数据 ----------- |
  • 用一个系统调用同时监控多个 fd

  • 当某个 fd 就绪时,再进行对应的读写

  • select / poll / epoll 三种实现

2.5 异步 IO

复制代码
用户进程                        内核

   |                             |
   |-- aio_read / io_uring ----->|
   |       (做其他事)             |-- 等待数据到达
   |                             |-- 数据拷贝到用户buf(内核完成)
   |<------ 完成通知/回调 -------  |
   |                             |
  • 真正的异步:发起请求后,内核完成全部工作(包括数据拷贝)后通知进程。

  • Linux 的实现:POSIX AIO、Linux AIO、io_uring

三、IO重要概念

3.1 阻塞与非阻塞

复制代码
阻塞IO : 因IO条件不满足(数据从网卡/磁盘还未到达内核缓冲区),而阻塞卡住,直到IO条件就绪。

非阻塞IO : 采用轮询模式判断IO条件是否满足,若检测到IO条件不具备,就出错返回。

3.2 同步与异步

复制代码
同步IO : 参与了IO的一个阶段或多个阶段过程 (等待数据 + 拷贝数据)

异步IO : 仅"发起IO" 或 "IO工作流"与当前工作流无关

四、非阻塞IO

4.1 fcntl 系统调用

复制代码
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );
  • 复制一个现有的描述符(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)。

4.2 代码示例

设置非阻塞模式

复制代码
#include <fcntl.h>
#include <unistd.h>
​
// 获取当前文件状态标志
int flags = fcntl(fd, F_GETFL, 0);
​
// 设置非阻塞标志
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

代码示例:

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
​
int main()
{
    char buf[1024];
    ssize_t n;
​
    // 将标准输入设置为非阻塞
    int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
​
    printf("尝试读取(非阻塞模式)...\n");
​
    while (1)
    {
        n = read(STDIN_FILENO, buf, sizeof(buf) - 1);
        if (n > 0)
        {
            buf[n] = '\0';
            printf("读到 %zd 字节: %s", n, buf);
        }
        else if (n == -1)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                printf("数据未就绪,稍后再试...\n");
                usleep(500000); // 500ms 后重试
            }
            else
            {
                perror("read error");
                break;
            }
        }
        else if (n == 0)
        {
            printf("EOF\n");
            break;
        }
    }
    return 0;
}
相关推荐
不会就选b1 小时前
Linux之TCP理论<1>
linux·运维·服务器
小π军2 小时前
操作系统面试题
java·linux·服务器
Lsetea3 小时前
curl用IP测试HTTPS报证书不匹配:--resolve与Host头别混用
linux·https·ssl证书·openssl·curl
Hao2853 小时前
Windows11 下把 WSL2 Ubuntu 26.04 装到 D 盘完整
linux
Android系统攻城狮3 小时前
Linux Gstreamer深度解析之gst_audio_encoder_get_frame_samples_min调用流程与实战(六十)
linux·运维·服务器·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
_upupup4 小时前
Linux中的自动化构建——make/Makefile
linux·运维·自动化
HEJOO95 小时前
Linux 设备驱动系列——使用全局工作队列
java·linux·算法
slimer5 小时前
07.进制转换上篇:位权展开与十进制互转
linux
顺风尿一寸5 小时前
手写迷你 Docker:用 Namespace + Cgroup v2 实现一个简单容器
linux