浅学磁盘IO

88 阅读6分钟

了解DMA技术

DMA(Direct Memory Access) 一种硬件技术,由独立的DMA控制器芯片(或集成在主板/SOC中)实现,详细信息可AI了解。

  • DMA是硬件加速数据传输的核心技术,它像一名“专职搬运工”,在磁盘与内存间直接搬数据,彻底解放CPU。
  • 数据加载流程:CPU发起请求 → DMA初始化 → 磁盘读取 → DMA传输 → 中断通知。
  • 现代计算机中几乎所有高速I/O设备(SSD、网卡、GPU)都依赖DMA提升性能。

DMA(Direct Memory Access,直接内存访问)机制,使得CPU不参与数据的移动,减少CPU开销

举一个,打印机的例子,看看DMA是如何减少CPU开销的 image.png

磁盘I/O - 读写磁盘

磁盘I/O 之 应用程序如何从磁盘中读取数据

image.png

磁盘I/O 之 应用程序如何向磁盘中写入数据

image.png

在举一个简单小例子:

磁盘IO的一个小例子.png

零拷贝

在把数据从磁盘(或文件)送到网卡(或 Socket)的过程中,CPU 不再把数据在“内核缓冲区 ↔ 用户缓冲区”之间来回复制,而是让 DMA 控制器直接把数据搬进/搬出内核页缓存,再(或同时)让网卡 SG-DMA 把页缓存里的数据搬去网卡,因此“用户态代码永远摸不到真正的 payload”,既省掉 2 次 CPU 复制,也省掉 2 次用户态-内核态上下文切换,于是称为 zero-copy image.png 一句话总结:

“零拷贝”并不是“数据没被复制”,而是把复制全部交给 DMA 在内核里完成,用户态代码 0 次 CPU 复制;C 用 sendfile/mmap/splice,C++ 直接包装它们,Java NIO 则用FileChannel.transferTo/transferFrom 即可

拓展:I/O 还有很多

I/O 操作的核心是“输入/输出”,它的本质是 数据在 CPU/内存 与 外部设备之间 的流动。 磁盘读写只是其中最常见、最典型的一种,但远不是全部。

可以把 CPU 和内存想象成公司的“总部办公室”,而所有其他设备都是“外部办事处或合作伙伴”。任何需要与这些“外部实体”进行的数据交换,都属于 I/O 操作。

以下是操作系统中主要的 I/O 操作类型:


1. 存储设备 I/O(你最熟悉的)

这是与持久化存储设备的数据交换。

  • 磁盘 I/O:读写硬盘(HDD)、固态硬盘(SSD)。例如:保存文件、加载程序、数据库查询。
  • 光盘 I/O:从 CD、DVD、蓝光光盘读取数据。
  • U盘/移动硬盘 I/O。

2. 网络 I/O

这是与网络设备的数据交换,是网络编程和互联网应用的基石。

  • 发送数据:将数据包通过网卡发送到网络上。
  • 接收数据:从网卡读取到来的数据包。
  • 例子:浏览网页、发送邮件、在线视频、网络游戏。这通常不被用户直观地感知为“I/O”,但它在系统资源占用和性能上极其重要。

3. 外设 I/O

这是与用户交互或专用功能设备的通信。

  • 键盘输入:你按下按键,对于 OS 来说就是一个输入操作。
  • 鼠标输入:移动鼠标、点击按键,也是一系列输入操作。
  • 显示器输出:GPU 将渲染好的帧数据输出到显示器。
  • 打印机输出:将文档数据发送给打印机。
  • 扫描仪输入:从扫描仪获取图像数据。
  • 音响/耳机输出:声卡将数字音频信号输出到音响设备。
  • 麦克风输入:声卡从麦克风录制音频信号。

4. 进程间通信(IPC - Inter-Process Communication)

这是在同一台机器上,不同进程之间交换数据。虽然数据可能没有离开主机,但它跨越了进程的“边界”,所以也是一种 I/O。

  • 管道:一个进程的输出作为另一个进程的输入(如 shell 中的 | 操作符)。
  • 消息队列
  • 共享内存

一个重要的概念:为什么 I/O 很“慢”?

理解 I/O 的关键在于认识到它与 CPU 处理速度的巨大差异。

  • CPU 和内存(纳秒级):处理速度极快,在纳秒级别。
  • I/O 设备(毫秒级甚至秒级):
    • 机械硬盘寻道需要毫秒级。
    • 网络请求可能需要几十到几百毫秒。
    • 等待用户键盘输入可能需要几秒甚至几分钟。

这个速度差距是几个数量级的! 因此,如何高效地管理 I/O,避免让高速的 CPU 白白等待低速的 I/O 设备,就成了操作系统设计中的一个核心问题。这就引出了以下几种 I/O 模型:


操作系统中的五种主要 I/O 模型

这些模型定义了应用程序如何与操作系统协作来完成一个 I/O 操作。

1. 阻塞 I/O :

  • 应用发起 I/O 调用后,线程被挂起,一直等待数据准备好并从内核缓冲区拷贝到用户空间后,才继续执行。
  • 最简单,但性能最差,一个线程只能处理一个 I/O 流。

2. 非阻塞 I/O :

  • 应用发起 I/O 调用后,立即返回一个错误码,不会阻塞线程。
  • 应用程序需要不断地轮询内核,询问数据是否准备好。这会消耗大量 CPU。

3. I/O 多路复用 :

  • 这是 select、poll、epoll 等系统调用的核心思想。
  • 应用将一个或多个 I/O 请求(如多个网络连接)注册到一个“代理”上(如 epoll)。
  • 然后这个“代理”会阻塞,等待任何一个被注册的 I/O 准备就绪,然后通知应用程序哪些 I/O 可以读了/写了。
  • 这是构建高性能网络服务器的关键模型(如 Nginx、Redis),可以用单个线程管理成千上万的连接。

4. 信号驱动 I/O :

  • 应用发起一个 I/O 请求,并注册一个信号处理函数。请求发出后,线程继续执行。
  • 当内核数据准备好时,会向应用发送一个 SIGIO 信号。
  • 应用在信号处理函数中进行实际的 I/O 操作。

5. 异步 I/O :

  • 应用发起一个 I/O 请求后,立即返回,继续做其他事情。
  • 内核会完成整个 I/O 操作(包括等待数据和将数据拷贝到用户空间)。
  • 操作完成后,内核通过某种机制(如回调函数)通知应用。
  • 与信号驱动 I/O 的区别在于:AIO 是内核完成所有工作后通知;信号驱动是内核通知你“可以开始工作了”,你自己还得去拷贝数据。

总结

I/O 类型常见例子特点
存储 I/O读写硬盘文件持久化存储,速度慢(相对于内存)
网络 I/O网页请求、数据库远程连接高延迟,不稳定,是现代后端开发的重点
外设 I/O键盘、鼠标、显示器与用户交互,实时性要求高
进程间 I/O管道、共享内存进程间数据交换,速度较快

所以,I/O 是一个极其广泛的概念,涵盖了计算机与外界(包括用户、网络、其他设备)的所有数据交换。 理解不同类型的 I/O 及其背后的模型,对于开发高效、健壮的软件至关重要。