银河麒麟服务器操作系统IO机制详解

了解更多银河麒麟操作系统全新产品,请点击访问:

麒麟软件产品专区:https://www.kylinos.cn/productPc/

开发者专区:https://developer.kylinos.cn/

文档中心:https://document.kylinos.cn/document/center


目录

[一、I/O 流程概述](#一、I/O 流程概述)

[1.1、读 I/O 概述](#1.1、读 I/O 概述)

[1.2、写 I/O 概述](#1.2、写 I/O 概述)

[1.3、裸设备 IO 读写机制](#1.3、裸设备 IO 读写机制)

[1.3.1 裸设备 IO 的定义与特性](#1.3.1 裸设备 IO 的定义与特性)

[1.3.2 裸设备 IO 读概述](#1.3.2 裸设备 IO 读概述)

[1.3.3 裸设备 IO 写概述](#1.3.3 裸设备 IO 写概述)

1.4、IO超时及重试机制


一、I/O 流程概述

在 Linux 系统中,传统的访问方式是通过 write() 和 read() 两个系统调用实现的,通过 read() 函数读取文件到到缓存区中,然后通过 write() 方法把缓存中的数据输出到文件。

1.1、读 I/O 概述

以 BufferIO 为例(与之对应的是 DirectIO,不读写 PageCache),当应用程序执行 read 系统调用读取一块数据的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。如果数据不存在,则先将数据从磁盘加载数据到内核空间的读缓存(Read Buffer)中,再从读缓存拷贝到用户进程的页内存中,过程如下:

a、用户进程(用户态)发起读请求,并从用户态通过 read() 函数向 Kernel 发起 System Call,上下文从 user space 切换为 kernel space。

b、系统调用 read() 会触发相应的 VFS(Virtual Filesystem Switch) 函数,传递的参数有文件描述符和文件偏移量。通过文件描述符 fd 找到对应的 inode,检查文件可读性。

c、VFS 确定请求的数据是否已经在内存缓冲区中,若在,数据从内核复制至用户态 buffer,然后应用程序读取,若数据不在内存中,将文件偏移量转换为逻辑块号(LBA)。

d、假设内核必须从块设备上读取数据,这样内核就必须确定数据在物理设备上的位置。这由映射层(Mapping Layer)来完成。

e、内核通过通用块设备层(Generic Block Layer)在块设备上执行读操作,构造 bio 结构体,包含目标设备、LBA 范围、数据方向(读),启动 I/O 操作。传输请求的数据。

f、在通用块设备层之下是 I/O 调度层(I/O Scheduler Layer),根据内核的调度策略,对等待的 I/O 等待队列排序和合并。

g、块设备驱动(Block Device Driver)驱动将逻辑块号转换为物理扇区地址,启动 DMA 传输,数据流向为:磁盘 → 控制器缓存 → Page Cache(由 DMA 完成),若为同步读,进程阻塞等待数据就绪。

h、最后,磁盘中断触发后,将 Page Cache 数据复制到用户缓冲区,唤醒进程,返回读取结果。

1.2、写 I/O 概述

a、用户进程调用 write() 将数据从用户缓冲区拷贝至内核 Page Cache(除非使用 mmap)。

b、VFS 层校验文件可写性,更新 inode 的修改时间标记。

c、数据暂存于内核缓存,对应的内存页标记为"脏页"(Dirty Page)并做元数据更新,比如文件大小、修改时间等元数据标记为脏。

d、此时如果是异步写,立即返回成功。

e、系统根据脏页留存时间、占用比例、系统内存压力等触发机制决定何时将脏页写入磁盘。当触发条件满足时,内核线程 flusher 被唤醒执行刷盘操作。

f、同步写则会阻塞进程,等待脏页写入磁盘后才返回。

g、对支持日志的文件系统如 ext4、xfs 等进行日志处理。

h、通用块层与 I/O 调度,合并多个脏页对应的写请求,生成大块 bio 请求。

i、设备驱动与磁盘写入,DMA 将数据从 Page Cache 传输到磁盘控制器缓存。磁盘控制器写入物理扇区,校验数据完整性(如 CRC)。

j、磁盘中断通知内核写入完成。除脏页标记,释放 Page Cache 空间(若需回收内存)。返回写入结果给用户进程。

1.3、裸设备 IO 读写机制

1.3.1 裸设备 IO 的定义与特性

裸设备(Raw Device)IO 是一种直接访问块设备的方式,允许应用程序绕过文件系统层,直接对底层存储设备进行读写操作。裸设备通常用于需要高性能、低延迟的应用场景,例如数据库管理系统(如 Oracle、MySQL 等)。

其与普通文件 I/O 的区别如下:

特性 裸设备 IO 普通文件 I/O
是否依赖文件系统 不依赖文件系统,直接访问块设备 依赖文件系统,通过文件路径访问
缓存机制 无缓存,数据直接从用户缓冲区传输到磁盘或从磁盘传输到用户缓冲区 数据会经过 Page Cache,操作系统负责缓存管理和同步
性能 更高效,适合低延迟、高吞吐量的场景 性能稍低,但借助文件系统缓存可提高重复读取的效率
元数据管理 无元数据管理,应用程序需手动维护数据一致性 文件系统自动管理元数据(如 inode、文件大小、权限等)
开发复杂度 开发复杂度高,需手动管理数据布局、一致性及错误恢复 开发简单,文件系统提供高级抽象,开发者无需关注底层细节
适用场景 数据库事务日志、高性能计算、实时性要求高的应用 普通文件读写、日常应用程序
数据一致性 应用程序需自行保证数据一致性 文件系统提供一致性保障,崩溃后可通过日志恢复
块大小限制 需遵循设备的块大小(如 512 字节或 4KB),对齐要求严格 文件系统屏蔽了块大小限制,开发者无需关心
错误处理 错误处理由应用程序负责 文件系统和操作系统提供错误处理机制

1.3.2 裸设备 IO 读概述

a、用户进程通过 open() 以 O_DIRECT 标志打开设备文件,获取文件描述符。

b、用户进程调用 read()/pread() 函数,指定要读取的设备偏移量和长度。读取请求必须按设备扇区大小对齐。

c、VFS 层验证文件描述符权限,识别这是对设备文件的操作,并绕过常规文件系统处理路径。

d、绕过文件系统缓存,直接创建读取请求。

e、创建 bio 请求结构体,描述数据传输的来源(设备扇区)和目标(用户空间缓冲区)。

f、通用块层处理 bio 请求,应用 I/O 调度策略优化访问顺序。

g、块设备驱动将 bio 请求转换为设备特定的命令。

h、设备读取指定扇区的数据,通过 DMA 直接传输到存储设备控制器缓存。

i、读取完成后设备触发中断,中断处理程序通知内核操作已完成。

j、内核向用户进程返回读取的字节数或错误码。

1.3.3 裸设备 IO 写概述

a、用户进程通过 open() 以 O_DIRECT 标志打开设备文件(如 /dev/sda),获取文件描述符。与常规 IO 不同,这里打开的是设备节点而非普通文件。

b、用户进程调用 write()/pwrite()函数,指定写入设备的偏移量和数据。由于是裸设备访问,数据必须按照设备扇区大小对齐(通常为 512 字节或 4KB)。

c、VFS 层验证文件描述符权限,识别这是对设备文件的操作,并绕过常规文件系统处理路径。

d、跳过了文件系统层,不存在 Page Cache 和脏页的概念,数据直接从用户空间准备传输到设备。

e、系统检查对齐要求和访问权限,确保写入操作合法。裸设备访问通常需要 root 权限或特定的设备访问权限。

f、调用块设备驱动的请求函数,创建 bio(Block I/O)请求结构体,描述数据传输信息。

g、通用块层接收 bio 请求,根据设备队列策略进行 I/O 调度,可能合并相邻请求以提高效率。

h、块设备驱动将 bio 请求转换为具体硬件操作,通常通过 DMA 直接将数据从用户空间传输到设备控制器。

i、设备执行物理写入操作,将数据写入指定扇区。完成后触发中断。

j、中断处理程序接收完成信号,通知内核 I/O 操作已完成。

k、内核通知用户进程写入完成,返回写入的字节数或错误码。

1.4、IO超时及重试机制

IO栈整体架构如下图:

  • 工作流程

I/O 发起 :用户调用 read() → VFS → 文件系统 → Block 层提交请求。

超时触发:Block 层检测到请求超时 → 调用设备驱动的错误处理。

设备恢复:驱动尝试重置设备/重试命令 → 成功则继续,失败则标记错误。

错误 上报 :Block 层清理请求 → 文件系统处理错误(如 ext4 记录日志错误)→ VFS 返回用户态 -ETIMEDOUT-EIO

  • VFS层

VFS 之上的系统调用 read/write 通常是阻塞的,无超时配置。VFS 层是虚拟文件系统层,主要职责是抽象和路由请求到对应的文件系统,VFS 本身不处理超时和重试,如果设备一直无响应,可能体现为 IO hung 住。一些文件系统可能自行实现超时重传,如 NFS 客户端可以通过设置 mount -o timeo,retrans 来设置超时和重试时间。

  • 块设备层

块设备层负责管理请求队列、合并请求、调度策略等。每个 IO 关联一个默认 30 秒的定时器。超时后触发回调;块设备层一般不重试,由驱动或者上层应用重试。

scsi 设备存在 IO 处理超时时间,用户空间可以查看并修改:

cat /sys/block/sdx/device/timeout,默认为 90s

echo -n 60 > /sys/block/sdc/device/timeout

  • 驱动层

驱动层负责与物理硬件交互,处理协议级超时。驱动层存在一些如 SCSI/NVME 命令超时配置,默认为 30s,命令超时会触发驱动的 error handler。

其中,nvme 设备超时时间可以被查看和修改:

cat /sy s/module/nvme_core/parameters/io_timeout,默认为 30s

echo -n 30 > /sys/module/nvme_core/parameters/io_timeout

相关推荐
fengye2071611 小时前
MongoDB 安装与配置
数据库·mongodb
sc_爬坑之路1 小时前
redis windows环境配置读写分离:一主一从 + Sentinel 完整实战
windows·redis·sentinel
OxyTheCrack2 小时前
【C++】一篇文章悲观锁与乐观锁与其思想在C++语言中的应用
linux·开发语言·数据库·c++·笔记
执笔论英雄2 小时前
【cuda】 pinpaged
android·java·数据库
一招定胜负2 小时前
大模型的API调用
数据库
czlczl200209252 小时前
插入时先写DB后写Redis?分布式中传统双写模式的缺陷
数据库·redis·分布式
数据知道2 小时前
MongoDB分片集群备份与恢复:复杂环境下的数据保护方案详解
数据库·mongodb
桌面运维家2 小时前
Linux VHD 虚拟磁盘更新指南:高效管理与优化
linux·运维·数据库
宇灬宇2 小时前
Oracle 到 PostgreSQL迁移(ora2pg)
数据库·postgresql·oracle