I/O系统架构深度解析
课程概述
本教程全面讲解计算机I/O系统的工作原理,从总线架构演进到DMA机制,从中断处理到I/O虚拟化,帮助你深入理解I/O子系统的性能优化策略。
学习目标:
- 理解总线架构的演进历程
- 掌握PCIe的分层协议和通道机制
- 深入了解DMA数据传输原理
- 学会中断处理机制与优化
- 掌握I/O虚拟化技术(SR-IOV)
1. 总线架构演进
1.1 总线技术发展史
┌─────────────────────────────────────────────────────────────┐
│ 总线架构演进时间线 │
└─────────────────────────────────────────────────────────────┘
1981 1992 2001 2003 2010 2017 2022
│ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼
ISA PCI PCI-X PCIe 1.0 PCIe 3.0 PCIe 4.0 PCIe 5.0
8MB/s 133MB/s 1GB/s 250MB/s 984MB/s 1.97GB/s 3.94GB/s
并行 并行 并行 串行(x1) 串行(x1) 串行(x1) 串行(x1)
带宽对比(x16通道):
PCI 2.1 GB/s ████
PCI-X 8.5 GB/s ████████████████
PCIe 1.0 4.0 GB/s ███████
PCIe 2.0 8.0 GB/s ██████████████
PCIe 3.0 15.8 GB/s ███████████████████████████
PCIe 4.0 31.5 GB/s ██████████████████████████████████████████████████████
PCIe 5.0 63.0 GB/s ████████████████████████████████████████████████████████████████████████████████████████████████████
架构对比:
传统PCI(并行总线):
┌──────────────────────────────────────────┐
│ CPU │
│ │ │
│ ▼ │
│ 北桥(MCH) │
│ │ │
│ ├────────────────────────┐ │
│ │ PCI总线(共享) │ 32/64位 │
│ │ ═══════════════ │ 33/66MHz │
│ │ │ │ │ │ │
│ │ 设备1 设备2 设备3 │ 所有设备 │
│ │ │ 竞争带宽 │
│ │ 南桥(ICH) │ │
│ └────────────────────────┘ │
└──────────────────────────────────────────┘
现代PCIe(点对点串行):
┌──────────────────────────────────────────┐
│ CPU │
│ │ │
│ ├─────┬─────┬─────┬─────┐ │
│ │ │ │ │ │ │
│ x16 x8 x4 x4 x1 │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ GPU SSD 网卡 声卡 USB │
│ │
│ 每个设备独享通道(无竞争) │
└──────────────────────────────────────────┘1.2 PCIe拓扑结构
┌─────────────────────────────────────────────────────────────┐
│ PCIe拓扑架构(PCIe Topology) │
└─────────────────────────────────────────────────────────────┘
完整系统拓扑:
┌─────────────┐
│ CPU │
│ ┌───────┐ │
│ │ PCIe │ │ Root Complex
│ │ Root │ │ (根复合体)
└──┴───┬───┴──┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│PCIe x16 │ │PCIe x8 │ │PCIe x4 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────────┐
│ GPU │ │ Switch │ │ NVMe SSD │
│ │ └────┬────┘ └─────────────┘
│ │ │
└─────────┘ ┌─────┴─────┐
│ │
┌────▼────┐ ┌────▼────┐
│ 网卡 │ │ RAID │
│ x4 │ │ 卡 x4 │
└─────────┘ └─────────┘
层次结构术语:
┌─────────────────────────────────────────┐
│ Root Complex (RC) │ CPU集成
│ │ │
│ ├─ Endpoint (设备) │ 单功能设备
│ │ │
│ ├─ Switch (交换机) │ 扩展端口
│ │ └─ Virtual Bridge (虚拟桥) │
│ │ │
│ └─ Legacy Endpoint │ 兼容设备
└─────────────────────────────────────────┘
地址空间:
┌──────────────────────────────────────────┐
│ Configuration Space (配置空间) │
│ - 256B (PCIe 1.x) │
│ - 4KB (PCIe 2.0+扩展配置空间) │
│ - 存储设备ID、BAR等 │
│ │
│ Memory Space (内存空间) │
│ - MMIO映射 │
│ - 设备寄存器访问 │
│ │
│ I/O Space (I/O空间) │
│ - 传统I/O端口(逐渐淘汰) │
└──────────────────────────────────────────┘2. PCIe协议详解
2.1 PCIe分层架构
┌─────────────────────────────────────────────────────────────┐
│ PCIe协议栈(PCIe Protocol Stack) │
└─────────────────────────────────────────────────────────────┘
OSI风格分层:
┌──────────────────────────────────────────────────────────┐
│ Transaction Layer (事务层) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ - 生成/接收TLP (Transaction Layer Packet) │ │
│ │ - 管理信用流控(Flow Control) │ │
│ │ - QoS虚拟通道(Virtual Channels) │ │
│ │ - 排序规则(Ordering Rules) │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Data Link Layer (数据链路层) │ │
│ │ - 添加/验证序列号和CRC │ │
│ │ - ACK/NAK协议 │ │
│ │ - 重传机制 │ │
│ │ - 封装为DLLP (Data Link Layer Packet) │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Physical Layer (物理层) │ │
│ │ - 8b/10b编码(PCIe 1.x/2.x) │ │
│ │ - 128b/130b编码(PCIe 3.0+) │ │
│ │ - 差分信号传输 │ │
│ │ - 链路训练和状态机 │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 物理介质(铜线/光纤) │
└──────────────────────────────────────────────────────────┘
TLP包结构(Memory Read Request示例):
┌─────────────────────────────────────────────────────┐
│ TLP Header (12-16 bytes) │
│ ┌──────┬──────┬───────┬─────────┬─────────────┐ │
│ │ Fmt │ Type │ Length│Requester│ Address │ │
│ │ (2b) │ (5b) │(10b) │ ID (16b)│ (32/64b) │ │
│ └──────┴──────┴───────┴─────────┴─────────────┘ │
│ │
│ Data Payload (0-4096 bytes) │
│ ┌─────────────────────────────────────────────┐ │
│ │ 实际数据 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ECRC (可选,4 bytes) │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
TLP类型:
┌────────────────────┬─────────────────────────┐
│ 内存请求 │ Memory Read/Write │
│ I/O请求 │ I/O Read/Write │
│ 配置请求 │ Configuration Read/Write│
│ 消息请求 │ Message (中断等) │
│ 完成 │ Completion (响应) │
└────────────────────┴─────────────────────────┘2.2 PCIe通道与带宽
┌─────────────────────────────────────────────────────────────┐
│ PCIe通道配置(Lane Configuration) │
└─────────────────────────────────────────────────────────────┘
物理通道(Lane):
每个Lane = 1对差分信号(TX + RX)
Lane配置:
┌─────────────────────────────────────┐
│ x1: ─→ (1个Lane) │
│ ←─ │
│ │
│ x4: ═══→ (4个Lane并行) │
│ ←═══ │
│ │
│ x8: ═══════→ (8个Lane) │
│ ←═══════ │
│ │
│ x16: ═══════════════→ (16个Lane) │
│ ←═══════════════ │
└─────────────────────────────────────┘
带宽计算:
┌──────────────────────────────────────────────────────┐
│ PCIe世代 编码方式 单Lane速率 x16带宽 │
├──────────────────────────────────────────────────────┤
│ 1.0 8b/10b(80%) 250MB/s 4.0 GB/s │
│ 2.0 8b/10b(80%) 500MB/s 8.0 GB/s │
│ 3.0 128b/130b 984MB/s 15.8 GB/s │
│ (98.5%) │
│ 4.0 128b/130b 1.97GB/s 31.5 GB/s │
│ 5.0 128b/130b 3.94GB/s 63.0 GB/s │
│ 6.0 FLIT+PAM4 7.88GB/s 126.0 GB/s │
└──────────────────────────────────────────────────────┘
编码效率示例:
┌────────────────────────────────────┐
│ 8b/10b编码(PCIe 1.x/2.x): │
│ 每8位数据编码为10位传输 │
│ 效率:8/10 = 80% │
│ 原因:用于时钟恢复和DC平衡 │
│ │
│ 128b/130b编码(PCIe 3.0+): │
│ 每128位数据添加2位同步头 │
│ 效率:128/130 = 98.5% │
│ 改进:更高效率,更复杂 │
└────────────────────────────────────┘
实际可用带宽:
┌──────────────────────────────────────┐
│ 理论带宽需要减去协议开销: │
│ - TLP头部(12-20字节) │
│ - 数据链路层开销(序列号、CRC) │
│ - ACK/NAK开销 │
│ │
│ 有效载荷比例:~85-90% │
│ │
│ 示例(PCIe 3.0 x4 NVMe SSD): │
│ 理论带宽:3.94 GB/s │
│ 实际带宽:~3.5 GB/s │
└──────────────────────────────────────┘
热插拔与链路训练:
┌────────────────────────────────────┐
│ 1. 检测阶段(Detect) │
│ - 检测设备插入 │
│ │
│ 2. 轮询阶段(Polling) │
│ - 协商速率和Lane数量 │
│ │
│ 3. 配置阶段(Configuration) │
│ - 交换Link能力 │
│ - 配置Lane映射 │
│ │
│ 4. L0工作状态 │
│ - 正常数据传输 │
│ │
│ 5. 省电状态 │
│ - L0s: 微秒级恢复 │
│ - L1: 毫秒级恢复 │
│ - L2: 需要重新训练 │
└────────────────────────────────────┘3. DMA直接内存访问
3.1 DMA工作原理
┌─────────────────────────────────────────────────────────────┐
│ DMA工作流程(DMA Operation) │
└─────────────────────────────────────────────────────────────┘
传统PIO(Programmed I/O)模式:
┌────────────────────────────────────────┐
│ 1. CPU发起读取请求 │
│ │ │
│ ▼ │
│ 2. 设备准备数据 │
│ │ │
│ ▼ │
│ 3. CPU循环检查状态(轮询) │
│ │ while(!ready) {} │
│ ▼ │
│ 4. CPU读取数据到寄存器 │
│ │ │
│ ▼ │
│ 5. CPU写入内存 │
│ │ │
│ └─→ 重复直到所有数据传输完成 │
│ │
│ 缺点:CPU全程参与,浪费CPU资源 │
└────────────────────────────────────────┘
DMA模式(CPU解放):
┌────────────────────────────────────────────────────┐
│ CPU DMA控制器 设备 │
│ │ │ │ │
│ ├─1. 配置DMA──────────→│ │ │
│ │ (源地址、目标地址、 │ │ │
│ │ 传输大小) │ │ │
│ │ │ │ │
│ │ ├─2. 请求数据─────→│ │
│ │ │ │ │
│ │ │←─3. 数据────────┤ │
│ │ │ │ │
│ │ ├─4. 写入内存─────→内存 │
│ │ │ (总线仲裁) │ │
│ │ │ │ │
│ │ │ ...重复... │ │
│ │ │ │ │
│ │←─5. 中断通知完成─────┤ │ │
│ │ │ │ │
│ └─6. 处理完成的数据 │ │ │
│ │
│ 优点:CPU只需配置和处理结果,传输期间可做其他任务 │
└────────────────────────────────────────────────────┘
DMA传输类型:
┌──────────────────────────────────────────┐
│ 1. 单次DMA (Single Transfer) │
│ 每个DMA请求传输1字节/字 │
│ │
│ 2. 块DMA (Block Transfer) │
│ 传输整个数据块后释放总线 │
│ │
│ 3. 周期窃取DMA (Cycle Stealing) │
│ 在CPU不使用总线时窃取周期 │
│ │
│ 4. 突发DMA (Burst Mode) │
│ 占用总线传输整个数据块(最快) │
└──────────────────────────────────────────┘
Scatter-Gather DMA:
┌──────────────────────────────────────────┐
│ 问题:数据分散在内存多个不连续位置 │
│ │
│ 内存布局: │
│ ┌────┐ │
│ │Buf1│ 地址0x1000, 长度4KB │
│ └────┘ │
│ ... (中间有其他数据) │
│ ┌────┐ │
│ │Buf2│ 地址0x5000, 长度8KB │
│ └────┘ │
│ ... │
│ ┌────┐ │
│ │Buf3│ 地址0xA000, 长度2KB │
│ └────┘ │
│ │
│ Scatter-Gather列表: │
│ ┌───────────┬──────┬────────┐ │
│ │ 地址 │ 长度 │ 标志 │ │
│ ├───────────┼──────┼────────┤ │
│ │ 0x1000 │ 4KB │ More │ │
│ │ 0x5000 │ 8KB │ More │ │
│ │ 0xA000 │ 2KB │ Last │ │
│ └───────────┴──────┴────────┘ │
│ │
│ DMA引擎自动处理多个分散的缓冲区 │
└──────────────────────────────────────────┘3.2 IOMMU与DMA重映射
┌─────────────────────────────────────────────────────────────┐
│ IOMMU架构(I/O Memory Management Unit) │
└─────────────────────────────────────────────────────────────┘
无IOMMU的问题:
┌────────────────────────────────────────┐
│ 设备使用物理地址进行DMA │
│ │
│ 问题: │
│ 1. 安全风险:设备可访问所有物理内存 │
│ 2. 地址限制:32位设备无法访问4GB以上 │
│ 3. 虚拟化困难:无法隔离VM的设备 │
└────────────────────────────────────────┘
IOMMU解决方案(Intel VT-d / AMD-Vi):
┌────────────────────────────────────────────────────┐
│ 设备视角 │
│ ┌────────┐ │
│ │ NIC │ 发起DMA到地址0x1000 │
│ └────┬───┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ IOMMU │ 地址转换 │
│ │ │ 0x1000 → 0x8A3F1000 (物理地址) │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 物理内存 │ │
│ │ 0x8A3F1000处 │ 实际写入位置 │
│ └─────────────────┘ │
│ │
│ 优势: │
│ - 设备隔离(每个设备独立地址空间) │
│ - 内存保护(设备只能访问授权区域) │
│ - 支持虚拟化(SR-IOV直通) │
└────────────────────────────────────────────────────┘
DMA地址映射示例:
┌────────────────────────────────────────┐
│ 驱动程序申请DMA缓冲区: │
│ │
│ dma_addr_t dma_handle; │
│ void *cpu_addr; │
│ │
│ cpu_addr = dma_alloc_coherent( │
│ dev, │
│ size, │
│ &dma_handle, ← 设备使用这个地址 │
│ GFP_KERNEL │
│ ); │
│ │
│ CPU使用cpu_addr访问 │
│ 设备使用dma_handle访问 │
│ IOMMU负责地址转换 │
└────────────────────────────────────────┘
IOMMU页表结构(类似CPU页表):
┌──────────────────────────────────┐
│ 设备虚拟地址 (IOVA) │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ IOMMU页表 │ 多级页表 │
│ │ 转换 │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ 物理地址 │
└──────────────────────────────────┘4. 中断处理机制
4.1 中断类型与演进
┌─────────────────────────────────────────────────────────────┐
│ 中断机制演进(Interrupt Evolution) │
└─────────────────────────────────────────────────────────────┘
传统中断(Legacy INTx):
┌────────────────────────────────────┐
│ 物理中断引脚:INTA#, INTB#, ... │
│ │
│ ┌────────┐ │
│ │ 设备1 ├───INTA#──┐ │
│ └────────┘ │ │
│ ┌────────┐ │ │
│ │ 设备2 ├───INTB#──┤ │
│ └────────┘ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 中断控制器 │ │
│ │ (PIC) │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ CPU │
│ │
│ 缺点: │
│ - 中断线数量有限(共享中断) │
│ - 中断风暴问题 │
│ - 不支持多核亲和性 │
└────────────────────────────────────┘
MSI (Message Signaled Interrupts):
┌────────────────────────────────────┐
│ 通过写内存触发中断(无物理引脚) │
│ │
│ ┌────────┐ │
│ │ 设备 │ │
│ │ │ │
│ │ 中断 │ │
│ │ 发生 │ │
│ └────┬───┘ │
│ │ │
│ ├─写入特定内存地址────┐ │
│ │ (0xFEE00000系列) │ │
│ │ 附带中断向量号 │ │
│ │ │ │
│ ▼ ▼ │
│ PCIe总线 内存控制器 │
│ │ │ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ LAPIC (CPU) │
│ │
│ 优势: │
│ - 每设备独立中断向量 │
│ - 无需物理引脚 │
│ - 支持多达32个中断 │
└────────────────────────────────────┘
MSI-X (Extended MSI):
┌────────────────────────────────────┐
│ MSI增强版 │
│ │
│ MSI-X表(设备内存映射区域): │
│ ┌────────┬──────────┬─────────┐ │
│ │ Entry │ 地址 │ 数据 │ │
│ ├────────┼──────────┼─────────┤ │
│ │ 0 │0xFEE00020│ Vector1 │ │
│ │ 1 │0xFEE00040│ Vector2 │ │
│ │ ... │ ... │ ... │ │
│ │ 2047 │0xFEE00xxx│ VectorN │ │
│ └────────┴──────────┴─────────┘ │
│ │
│ 特性: │
│ - 最多2048个中断向量 │
│ - 每个中断独立配置 │
│ - 支持CPU亲和性 │
│ - 屏蔽位(单独控制每个中断) │
│ │
│ 应用:高性能网卡(每队列一个中断) │
└────────────────────────────────────┘
中断亲和性配置:
┌────────────────────────────────────┐
│ 将设备中断绑定到特定CPU核心 │
│ │
│ 示例(网卡多队列): │
│ ┌──────────┐ │
│ │ 网卡 │ │
│ │ ┌──┐┌──┐│ │
│ │ │Q0││Q1││ │
│ │ └┬─┘└┬─┘│ │
│ └──┼───┼──┘ │
│ │ │ │
│ 中断 中断 │
│ 0 1 │
│ │ │ │
│ ┌──▼───▼───────────┐ │
│ │ CPU │ │
│ │ ┌───┐ ┌───┐ │ │
│ │ │CPU0│ │CPU1│ │ │
│ │ │IRQ0│ │IRQ1│ │ │
│ │ └───┘ └───┘ │ │
│ └──────────────────┘ │
│ │
│ /proc/irq/N/smp_affinity 配置 │
└───────────────────────────────────┘4.2 中断处理流程
┌─────────────────────────────────────────────────────────────┐
│ Linux中断处理(Interrupt Handling) │
└─────────────────────────────────────────────────────────────┘
中断处理分层:
┌────────────────────────────────────────┐
│ 硬件中断发生 │
│ │ │
│ ▼ │
│ 1. CPU保存现场 │
│ - 保存寄存器 │
│ - 切换到中断栈 │
│ │ │
│ ▼ │
│ 2. 执行中断向量表ISR │
│ do_IRQ() │
│ │ │
│ ▼ │
│ 3. 上半部处理 (Top Half) │
│ ┌─────────────────────────────┐ │
│ │ - 快速、原子操作 │ │
│ │ - 禁止中断 │ │
│ │ - 清除中断标志 │ │
│ │ - 读取设备状态 │ │
│ │ - 调度下半部 │ │
│ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ 4. 恢复现场返回 │
│ │ │
│ ▼ │
│ 5. 下半部处理 (Bottom Half) │
│ ┌─────────────────────────────┐ │
│ │ 可选机制: │ │
│ │ - Softirq (软中断) │ │
│ │ - Tasklet │ │
│ │ - Workqueue (工作队列) │ │
│ │ │ │
│ │ - 慢速、可睡眠 │ │
│ │ - 允许中断 │ │
│ │ - 复杂数据处理 │ │
│ │ - 唤醒应用进程 │ │
│ └─────────────────────────────┘ │
└────────────────────────────────────────┘
示例:网卡接收中断
┌────────────────────────────────────────┐
│ 中断上半部(硬中断上下文): │
│ │
│ irqreturn_t nic_interrupt( │
│ int irq, void *dev_id) │
│ { │
│ // 禁用设备中断 │
│ disable_device_interrupts(); │
│ │
│ // 读取中断状态 │
│ status = read_status_register(); │
│ │
│ // 调度软中断处理 │
│ napi_schedule(&napi); │
│ │
│ return IRQ_HANDLED; │
│ } │
│ │
│ 执行时间:<10μs │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 中断下半部(软中断上下文): │
│ │
│ int nic_poll(struct napi *napi, │
│ int budget) │
│ { │
│ int processed = 0; │
│ │
│ // 循环处理接收队列 │
│ while (processed < budget) { │
│ skb = receive_packet(); │
│ if (!skb) break; │
│ │
│ netif_receive_skb(skb); │
│ processed++; │
│ } │
│ │
│ // 重新启用中断 │
│ if (processed < budget) { │
│ enable_device_interrupts(); │
│ } │
│ │
│ return processed; │
│ } │
│ │
│ 执行时间:可长(处理多个包) │
└────────────────────────────────────────┘
NAPI (New API) 轮询模式:
┌────────────────────────────────────────┐
│ 低负载:中断驱动 │
│ ┌────┐ ┌────┐ ┌────┐ │
│ │ 包 │ │ 包 │ │ 包 │ 每个包一次中断│
│ └─┬──┘ └─┬──┘ └─┬──┘ │
│ │中断 │中断 │中断 │
│ ▼ ▼ ▼ │
│ │
│ 高负载:轮询模式 │
│ ┌────┬────┬────┬────┬────┐ │
│ │ 包 │ 包 │ 包 │ 包 │ 包 │ 批量处理 │
│ └─┬──┴────┴────┴────┴────┘ │
│ │仅一次中断,后续轮询 │
│ ▼ │
│ │
│ 自适应:根据负载切换模式 │
└────────────────────────────────────────┘5. I/O虚拟化
5.1 SR-IOV技术
┌─────────────────────────────────────────────────────────────┐
│ SR-IOV (Single Root I/O Virtualization) │
└─────────────────────────────────────────────────────────────┘
无SR-IOV(传统虚拟化):
┌────────────────────────────────────────┐
│ VM1 VM2 VM3 │
│ │ │ │ │
│ └────────┼────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Hypervisor│ 软件模拟/半虚拟化 │
│ │ (QEMU) │ │
│ └─────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ 物理网卡 │ │
│ └─────────┘ │
│ │
│ 问题: │
│ - 性能开销大(上下文切换) │
│ - 延迟高 │
│ - CPU占用高 │
└────────────────────────────────────────┘
SR-IOV架构:
┌────────────────────────────────────────────────────┐
│ VM1 VM2 VM3 Hypervisor │
│ │ │ │ │ │
│ │ │ │ │ │
│ VF1 VF2 VF3 PF │
│ │ │ │ │ │
│ └───────────┴───────────┴──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 物理网卡(SR-IOV) │ │
│ │ │ │
│ │ ┌────────────────┐ │ │
│ │ │ PF (Physical │ │ 完整功能 │
│ │ │ Function) │ │ (管理/配置) │
│ │ └────────────────┘ │ │
│ │ │ │
│ │ ┌───┐┌───┐┌───┐ │ │
│ │ │VF1││VF2││VF3│ │ 轻量级副本 │
│ │ └───┘└───┘└───┘ │ (仅数据路径) │
│ │ ...最多256个VF │ │
│ └──────────────────────┘ │
│ │
│ 优势: │
│ - 接近原生性能(直通) │
│ - 低延迟 │
│ - 硬件隔离 │
└────────────────────────────────────────────────────┘
PF vs VF功能对比:
┌─────────────────────┬──────┬──────┐
│ 功能 │ PF │ VF │
├─────────────────────┼──────┼──────┤
│ 数据发送/接收 │ ✓ │ ✓ │
│ 配置寄存器访问 │ ✓ │ 限制 │
│ 中断 │ ✓ │ ✓ │
│ DMA │ ✓ │ ✓ │
│ 创建/销毁VF │ ✓ │ ✗ │
│ 全局配置 │ ✓ │ ✗ │
│ 固件更新 │ ✓ │ ✗ │
└─────────────────────┴──────┴──────┘
配置SR-IOV:
# 启用VF
echo 4 > /sys/class/net/eth0/device/sriov_numvfs
# 查看VF
lspci | grep Virtual
# 将VF分配给VM(libvirt)
<interface type='hostdev'>
<source>
<address type='pci' domain='0x0000'
bus='0x05' slot='0x10' function='0x1'/>
</source>
</interface>6. I/O性能测试
6.1 PCIe性能测试
bash
#!/bin/bash
# PCIe设备性能测试
echo "========== PCIe设备信息 =========="
# 查看所有PCIe设备
lspci -vvv | grep -E "LnkCap|LnkSta" | head -20
echo -e "\n========== NVMe设备PCIe信息 =========="
# 查看NVMe设备的PCIe链路状态
for dev in /dev/nvme*n1; do
if [ -e "$dev" ]; then
echo "=== $dev ==="
nvme id-ctrl $dev | grep -E "vid|ssvid"
# PCIe链路速度
dev_pci=$(readlink -f /sys/block/$(basename $dev)/device)
echo "PCIe Gen: $(cat $dev_pci/current_link_speed 2>/dev/null)"
echo "PCIe Width: $(cat $dev_pci/current_link_width 2>/dev/null)"
fi
done
echo -e "\n========== 网卡PCIe信息 =========="
# 查看网卡PCIe配置
for nic in /sys/class/net/*/device; do
if [ -e "$nic/current_link_speed" ]; then
nicname=$(basename $(dirname $nic))
echo "=== $nicname ==="
echo "Speed: $(cat $nic/current_link_speed)"
echo "Width: x$(cat $nic/current_link_width)"
fi
done
echo -e "\n========== PCIe带宽测试(理论值)=========="
# 计算理论带宽
# PCIe 3.0 x4 = 3.94 GB/s
# PCIe 4.0 x4 = 7.88 GB/s6.2 中断性能分析
bash
#!/bin/bash
# 中断性能分析脚本
echo "========== 中断统计 =========="
# 查看中断计数
cat /proc/interrupts | head -30
echo -e "\n========== 各CPU中断分布 =========="
# 显示每个CPU的总中断数
mpstat -P ALL 1 5 | grep -E "CPU|all"
echo -e "\n========== 软中断统计 =========="
# 查看软中断
cat /proc/softirqs
echo -e "\n========== 高频中断设备 =========="
# 找出中断最频繁的设备
watch -n 1 'cat /proc/interrupts | sort -k2 -rn | head -10'
echo -e "\n========== 中断亲和性检查 =========="
# 检查网卡中断亲和性
for irq in $(grep eth0 /proc/interrupts | cut -d: -f1); do
echo "IRQ $irq: CPU mask = $(cat /proc/irq/$irq/smp_affinity 2>/dev/null)"
done
echo -e "\n========== 优化中断亲和性 =========="
# 示例:将网卡队列中断绑定到不同CPU
# 假设eth0有4个队列
queue=0
for irq in $(grep eth0-TxRx /proc/interrupts | cut -d: -f1); do
cpu_mask=$(printf "%x" $((1 << queue)))
echo $cpu_mask > /proc/irq/$irq/smp_affinity
echo "IRQ $irq绑定到CPU $queue"
((queue++))
done6.3 I/O性能监控
python
#!/usr/bin/env python3
"""
I/O系统性能监控脚本
"""
import os
import time
import subprocess
def monitor_interrupts(duration=10):
"""监控中断速率"""
print("=== 中断速率监控 ===")
# 读取初始值
with open('/proc/interrupts') as f:
lines1 = f.readlines()
time.sleep(duration)
# 读取结束值
with open('/proc/interrupts') as f:
lines2 = f.readlines()
# 计算增量
print(f"\n{duration}秒内中断增量(Top 10):")
diffs = []
for l1, l2 in zip(lines1[1:], lines2[1:]):
cols1 = l1.split()
cols2 = l2.split()
if len(cols1) > 1 and cols1[0].endswith(':'):
irq = cols1[0][:-1]
# 累加所有CPU的中断数
try:
total1 = sum(int(x) for x in cols1[1:] if x.isdigit())
total2 = sum(int(x) for x in cols2[1:] if x.isdigit())
diff = total2 - total1
if diff > 0:
desc = ' '.join(cols2[len([x for x in cols2 if x.isdigit()]):])
diffs.append((irq, diff, desc))
except:
pass
diffs.sort(key=lambda x: x[1], reverse=True)
for irq, count, desc in diffs[:10]:
rate = count / duration
print(f" IRQ {irq:>4}: {rate:>10.0f} /秒 {desc[:50]}")
def monitor_pcie_errors():
"""监控PCIe错误"""
print("\n=== PCIe错误检查 ===")
try:
result = subprocess.run(
['lspci', '-vvv'],
capture_output=True,
text=True
)
for line in result.stdout.split('\n'):
if 'CorrErr' in line or 'UncErr' in line or 'Fatal' in line:
print(f" {line.strip()}")
except:
print("需要安装pciutils")
def monitor_dma_usage():
"""监控DMA使用情况"""
print("\n=== DMA缓冲区使用 ===")
try:
with open('/proc/meminfo') as f:
for line in f:
if 'Bounce' in line or 'DMA' in line:
print(f" {line.strip()}")
except:
pass
def monitor_io_metrics():
"""监控I/O指标"""
print("\n=== I/O设备指标 ===")
try:
result = subprocess.run(
['iostat', '-x', '1', '2'],
capture_output=True,
text=True
)
print(result.stdout)
except:
print("需要安装sysstat")
def check_msi_capability():
"""检查设备MSI/MSI-X能力"""
print("\n=== MSI/MSI-X支持情况 ===")
try:
result = subprocess.run(
['lspci', '-vvv'],
capture_output=True,
text=True
)
current_device = None
for line in result.stdout.split('\n'):
if line and not line.startswith('\t'):
current_device = line.split()[0]
elif 'MSI-X: Enable+' in line or 'MSI: Enable+' in line:
print(f" {current_device}: {line.strip()}")
except:
print("需要安装pciutils")
def main():
"""主函数"""
print("I/O系统性能监控")
print("=" * 60)
if os.geteuid() != 0:
print("警告:部分功能需要root权限")
print()
# 中断监控
monitor_interrupts(duration=5)
# PCIe错误
monitor_pcie_errors()
# DMA使用
monitor_dma_usage()
# I/O指标
monitor_io_metrics()
# MSI能力
check_msi_capability()
print("\n" + "=" * 60)
print("监控完成")
if __name__ == "__main__":
main()7. 学习资源与总结
7.1 关键要点总结
┌─────────────────────────────────────────────────────────────┐
│ I/O系统核心概念 │
└─────────────────────────────────────────────────────────────┘
1. 总线演进
├─ ISA → PCI → PCIe
├─ 并行 → 串行点对点
├─ 共享总线 → 独占通道
└─ PCIe 5.0: 63GB/s (x16)
2. PCIe协议
├─ 分层架构:事务层/链路层/物理层
├─ TLP数据包传输
├─ 通道配置:x1/x4/x8/x16
└─ 编码效率:128b/130b
3. DMA机制
├─ 解放CPU(无需参与数据传输)
├─ Scatter-Gather(分散聚集)
├─ IOMMU地址转换和隔离
└─ Coherent vs Streaming
4. 中断处理
├─ Legacy → MSI → MSI-X
├─ 上半部(快速)+ 下半部(慢速)
├─ NAPI轮询模式
└─ 中断亲和性优化
5. I/O虚拟化
├─ SR-IOV:硬件虚拟化
├─ PF vs VF
├─ 接近原生性能
└─ 硬件隔离
6. 性能优化
├─ PCIe链路速度检查
├─ 中断均衡分布
├─ IOMMU优化
└─ NUMA本地化
└─────────────────────────────────────────────────────────────┘下一步:学习GPU计算架构,理解CUDA编程和并行计算原理。
文件大小:约28KB 最后更新:2024年
💬 讨论
使用 GitHub 账号登录后即可参与讨论