Skip to content

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_OFFSET
    • phys_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: 有,但极少,仅在“生死攸关”的时刻。

  • 场景
    1. 内核启动早期 (head.S):从实模式/保护模式切换到长模式时。
    2. SMP 多核唤醒 (trampoline.S):从核刚醒来时处于实模式。
    3. Kexec / 休眠恢复:需要重置硬件状态时。
  • 关键技术恒等映射 (Identity Mapping)
    • 在开关分页的瞬间,建立 Virt == Phys 的映射,防止 CPU 指针因地址含义突变而崩溃。

4. 内存分配器 (kmalloc vs vmalloc)

Q: kmalloc 和 vmalloc 有什么区别?是否有页的概念?

A: 核心区别在于“物理连续性”和“分配路径”。

特性kmallocvmalloc
物理地址连续不连续 (离散物理页拼凑)
所在区域线性映射区 (Linear Mapping)vmalloc 专用区
寻址方式可直接用 __pa 宏转物理地址不能用宏,必须查页表
DMA 支持支持 (因为物理连续)不支持 (硬件通常需要连续物理地)
性能高 (无页表开销,TLB 友好)低 (需修改页表,TLB 易抖动)

Q: 两者都走 Slab 分配器吗?

A: 不是。

  • kmalloc走 Slab。它是 Slab 的主要客户,适合分配小对象。底层由 Slab 向伙伴系统申请页。
  • vmalloc不走 Slab (数据部分)。它直接向 伙伴系统 (Buddy System) 申请一个个独立的物理页,然后修改内核页表把它们拼在连续的虚拟地址上。
    • 注:vmalloc 管理自身的元数据结构 (struct vm_struct) 是通过 kmalloc 分配的,但负载数据不是。

kmalloc 更像是拆页,vmalloc 是在拼页,因此 kmalloc 会走 slab,vmalloc 可能就直接调用伙伴系统的接口拿页了。