DMA 概念与讲解
文章目录
- 一、Linux 物理内存区
- **1. 硬件兼容性与性能优化**
- **ZONE_DMA(直接内存访问区)**
- **ZONE_NORMAL(普通内存区)**
- **ZONE_HIGHMEM(高端内存区)**
- **2. 地址空间管理需求**
- **直接映射区(Direct Mapping)**
- **动态映射区(High Memory)**
- **3. 内存分配策略**
- **连续物理内存分配(`kmalloc`)**
- **非连续内存分配(`vmalloc`)**
- **Slab 分配器与对象缓存**
- **4. 跨架构差异**
- **5. 核心原则总结**
- **6. 现代系统的演变**
- **示例代码:查看内存区域信息**
- **总结**
- 参考
一、Linux 物理内存区
Linux 内核将物理内存划分为 ZONE_DMA、ZONE_NORMAL 和 ZONE_HIGHMEM 的原则,主要基于以下核心因素:
1. 硬件兼容性与性能优化
ZONE_DMA(直接内存访问区)
- 目的:支持老旧 DMA 控制器(Direct Memory Access)的硬件设备。
- 硬件限制:
许多早期 DMA 控制器(如 ISA 总线设备)仅能访问物理地址的低范围(通常为 0x00000000 ~ 0x00FFFFFF,即 16MB 或更低)。
若设备 DMA 操作超出此范围,可能导致地址溢出或数据损坏。 - 内核策略:
将物理内存的低地址段(如0x00000000 ~ 896MB
)保留为 ZONE_DMA,确保 DMA 设备可直接访问。
ZONE_NORMAL(普通内存区)
- 目的:存放内核核心数据和频繁访问的内存。
- CPU 架构优化:
现代 CPU 的 MMU(内存管理单元)和缓存机制对低端内存的访问效率更高。
内核代码、页表、全局数据结构(如task_struct
)需稳定且连续的物理内存,因此优先分配 ZONE_NORMAL。
ZONE_HIGHMEM(高端内存区)
- 目的:解决 32 位系统的物理内存寻址限制。
- 32 位系统限制:
32 位系统的虚拟地址空间为 4GB,内核直接映射的物理内存范围通常为0x00000000 ~ 0x7FFFFFFF
(即 896MB)。
超出部分(>896MB
)无法直接映射到内核虚拟地址空间,需通过动态映射(如vmalloc
或kmap
)访问,这部分称为 ZONE_HIGHMEM。 - 64 位系统差异:
64 位系统的虚拟地址空间极大(如 128TB),可直接映射全部物理内存,因此 无需 ZONE_HIGHMEM。
2. 地址空间管理需求
直接映射区(Direct Mapping)
- ZONE_DMA + ZONE_NORMAL:
内核启动时,物理内存的低地址段(如0x00000000 ~ high_memory
)被直接映射到内核虚拟地址空间(PAGE_OFFSET
起始)。
此区域访问高效,无需复杂的地址转换。
动态映射区(High Memory)
- ZONE_HIGHMEM(仅 32 位):
物理内存超过直接映射范围的部分(high_memory ~
),需通过临时映射(如kmap_atomic
)或页表切换访问。
此区域访问效率较低,但扩展了可用内存上限。
3. 内存分配策略
连续物理内存分配(kmalloc
)
- ZONE_DMA & ZONE_NORMAL:
kmalloc()
分配的物理内存默认来自 ZONE_DMA 或 ZONE_NORMAL,保证连续性和低延迟,适用于 DMA、内核数据结构等场景。
非连续内存分配(vmalloc
)
- ZONE_HIGHMEM(32 位):
vmalloc()
分配的虚拟内存可能映射到 ZONE_HIGHMEM,但物理页不要求连续,适用于大块内存或无需 DMA 的场景。
Slab 分配器与对象缓存
- 缓存亲和性:
频繁访问的小对象(如struct page
)优先从 ZONE_NORMAL 分配,减少缓存失效。
4. 跨架构差异
系统架构 | ZONE_DMA | ZONE_NORMAL | ZONE_HIGHMEM |
---|---|---|---|
32 位 | 低端内存(如 0~16MB) | 中段内存(16MB~896MB) | 高端内存(>896MB) |
64 位 | 通常空闲或极小 | 覆盖几乎全部物理内存 | 不存在 |
5. 核心原则总结
Linux 内核划分内存区域的三大原则:
- 硬件兼容性:
适配 DMA 设备等硬件的地址限制。 - 性能优化:
优先保障内核高频访问区域的连续性和低延迟。 - 地址空间高效利用:
在 32 位系统中通过 ZONE_HIGHMEM 扩展可用内存,64 位系统则简化管理。
6. 现代系统的演变
- 64 位系统:
因虚拟地址空间极大(如 48/57 位物理寻址),通常仅保留 ZONE_DMA 和 ZONE_NORMAL,ZONE_HIGHMEM 被淘汰。 - 大内存优化:
对 NUMA(非统一内存访问)系统,Linux 进一步细分 ZONE_NORMAL 为多个内存节点(Node),优化局部性。
示例代码:查看内存区域信息
#include <linux/mm.h>
#include <linux/printk.h>
void print_memory_zones(void) {
struct zone *zone;
for_each_zone(zone) {
pr_info("Zone: %s, Start: %lu KB, End: %lu KB
",
zone->name,
zone->zone_start_pfn << (PAGE_SHIFT - 10),
(zone->zone_start_pfn + zone->spanned_pages) << (PAGE_SHIFT - 10));
}
}
总结
Linux 内核通过划分 ZONE_DMA、ZONE_NORMAL 和 ZONE_HIGHMEM,实现了对硬件兼容性、性能及内存利用率的平衡。这种设计在 32 位系统中尤为重要,而在 64 位系统中则大幅简化,体现了操作系统对底层硬件的深刻理解和抽象能力。
参考
PCIe MMIO、DMA、TLP
【PCIe】PCIe配置空间与CPU访问机制
☆
Ctrl + Alt + V