Linux 内核内存管理核心机制总结
1. 物理内存管理 (Zones & Pages)
Q: Zone 的概念是基于物理内存层面的吗?
A: 是的。
- 用途:Zone 的划分是为了向硬件限制妥协(例如某些旧设备只能访问低端物理地址)。
- 层级:
Node(物理插槽) ->Zone(物理分区,如 DMA, NORMAL) ->Page(物理页帧)。 - 区别:
Zone(struct zone):管理物理内存分配。VMA(vm_area_struct):管理用户态进程的虚拟地址空间。
Q: struct page 实际上代表物理页吗?
A: 是的,一一对应。
- 关系:每一个 4KB 物理页帧都有一个对应的
struct page结构体。 - 内容:它存储的是元数据(Metadata),如引用计数、脏标记、LRU 链表节点等,而不是页内的数据。
- 开销:
struct page本身占用内存,约占总物理内存的 1.5% 左右。
2. 线性映射与寻址 (Linear Mapping)
Q: 内核是否有一部分数据常驻物理内存?寻址是直接减去一个很大的数?
A: 是的,这叫线性映射 (Linear Mapping)。
- 机制:在内核空间的“线性映射区”(Low Memory),虚拟地址和物理地址是线性对应的。
- 实现:
virt_to_phys(x)(即__pa):x - PAGE_OFFSETphys_to_virt(x)(即__va):x + PAGE_OFFSET
- 适用范围:仅适用于内核逻辑地址(如内核代码段、全局变量、
kmalloc内存),不适用于vmalloc或用户态地址。
Q: 既然直接减数值就能寻址,还需要走 MMU 吗?TLB Miss 怎么办?
A: 必须走 MMU,但利用了“大页”优化。
- 双层逻辑:
- 软件层:通过宏做减法计算,快速传递物理地址给硬件(如 DMA)。
- 硬件层:CPU 访问时必须经过 MMU 查页表。
- 性能保证:内核在启动时建立这张映射表时,使用了 Huge Pages (2MB 或 1GB)。
- 结果:TLB 条目极少,几乎总是 TLB Hit,性能损耗极低,不会频繁触发 Page Table Walk。
Q: 总结一下:访问时走 MMU,传值时用宏?
A: 精辟。
- 给自己用 (CPU 访问):走硬件 MMU 流程(虚拟地址)。
- 给别人用 (DMA/CR3):走软件宏计算(物理地址),因为外设不懂虚拟地址。
3. 分页机制的开启与关闭
Q: 内核中有没有代码是关掉分页的?
A: 有,但极少,仅在“生死攸关”的时刻。
- 场景:
- 内核启动早期 (
head.S):从实模式/保护模式切换到长模式时。 - SMP 多核唤醒 (
trampoline.S):从核刚醒来时处于实模式。 - Kexec / 休眠恢复:需要重置硬件状态时。
- 内核启动早期 (
- 关键技术:恒等映射 (Identity Mapping)。
- 在开关分页的瞬间,建立
Virt == Phys的映射,防止 CPU 指针因地址含义突变而崩溃。
- 在开关分页的瞬间,建立
4. 内存分配器 (kmalloc vs vmalloc)
Q: kmalloc 和 vmalloc 有什么区别?是否有页的概念?
A: 核心区别在于“物理连续性”和“分配路径”。
| 特性 | kmalloc | vmalloc |
|---|---|---|
| 物理地址 | 连续 | 不连续 (离散物理页拼凑) |
| 所在区域 | 线性映射区 (Linear Mapping) | vmalloc 专用区 |
| 寻址方式 | 可直接用 __pa 宏转物理地址 | 不能用宏,必须查页表 |
| DMA 支持 | 支持 (因为物理连续) | 不支持 (硬件通常需要连续物理地) |
| 性能 | 高 (无页表开销,TLB 友好) | 低 (需修改页表,TLB 易抖动) |
Q: 两者都走 Slab 分配器吗?
A: 不是。
kmalloc:走 Slab。它是 Slab 的主要客户,适合分配小对象。底层由 Slab 向伙伴系统申请页。vmalloc:不走 Slab (数据部分)。它直接向 伙伴系统 (Buddy System) 申请一个个独立的物理页,然后修改内核页表把它们拼在连续的虚拟地址上。- 注:vmalloc 管理自身的元数据结构 (
struct vm_struct) 是通过 kmalloc 分配的,但负载数据不是。
- 注:vmalloc 管理自身的元数据结构 (
kmalloc 更像是拆页,vmalloc 是在拼页,因此 kmalloc 会走 slab,vmalloc 可能就直接调用伙伴系统的接口拿页了。