了解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开销的
磁盘I/O - 读写磁盘
磁盘I/O 之 应用程序如何从磁盘中读取数据
磁盘I/O 之 应用程序如何向磁盘中写入数据
在举一个简单小例子:
零拷贝
在把数据从磁盘(或文件)送到网卡(或 Socket)的过程中,CPU 不再把数据在“内核缓冲区 ↔ 用户缓冲区”之间来回复制,而是让 DMA 控制器直接把数据搬进/搬出内核页缓存,再(或同时)让网卡 SG-DMA 把页缓存里的数据搬去网卡,因此“用户态代码永远摸不到真正的 payload”,既省掉 2 次 CPU 复制,也省掉 2 次用户态-内核态上下文切换,于是称为 zero-copy
一句话总结:
“零拷贝”并不是“数据没被复制”,而是把复制全部交给 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 及其背后的模型,对于开发高效、健壮的软件至关重要。