Linux swapper_pg_dir

在Linux内核中,swapper_pg_dir 是一个关键的数据结构,用于管理系统的页表(Page Tables)。以下是详细解释:

1. 定义与作用

  • 初始内核页表swapper_pg_dir 是内核启动时使用的 顶级页全局目录(Page Global Directory, PGD)。它在系统引导阶段初始化,负责建立内核空间的初始虚拟地址到物理地址的映射。
  • 内核空间映射:它直接映射内核代码、数据、设备内存(如vmalloc区域)等核心区域。所有进程共享内核空间的映射,因此swapper_pg_dir 的内核部分在进程切换时保持不变。

2. 生命周期与使用场景

  • 引导阶段:在系统启动时(如start_kernel()函数),内核通过swapper_pg_dir启用分页机制(MMU)。此时它提供临时的1:1线性映射(物理地址 = 虚拟地址 - 固定偏移)。
  • 运行时:系统初始化完成后,内核切换到完整的页表结构,但swapper_pg_dir仍保留:
    • 作为 内核主页表,为所有进程的内核空间提供统一映射。
    • 在进程切换时,用户空间的页表(PGD)会变化,但内核部分通过复制swapper_pg_dir的映射项保持一致性。

3. 代码中的位置

  • 架构相关实现
    • x86_64:定义于arch/x86/mm/pgtable.c,符号swapper_pg_dir
    • ARM64:定义于arch/arm64/kernel/vmlinux.lds.S(链接脚本),作为初始页表的存储位置。
  • 初始化:在paging_init()函数中填充页表项(如映射内核镜像、设备树等)。

4. 与进程页表的关系

每个进程的task_struct->mm_struct->pgd指向其私有页表:

  • 用户空间:独立映射,进程隔离。
  • 内核空间:直接从swapper_pg_dir复制而来(如fork()时通过pgd_alloc()复制内核部分),确保所有进程访问相同的内核地址。

5. 高级优化

  • 共享映射:现代内核(如x86_64的KAISER补丁后)使用独立的内核页表(init_top_pgt),但原理类似——仍存在全局内核页表。
  • TLB刷新:修改swapper_pg_dir时需广播TLB刷新(如flush_tlb_all()),保证多核一致性。

示例代码片段(x86_64)

1
2
3
4
5
6
7
8
// arch/x86/include/asm/pgtable_64_types.h
extern pud_t level3_kernel_pgt[512];
extern pmd_t level2_kernel_pgt[512];

// arch/x86/mm/pgtable.c
pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned_bss = {
[0 ... PTRS_PER_PGD - 1] = INIT_PGD
};

总结

swapper_pg_dir内核空间的全局页表根目录,核心作用包括:

  1. 引导阶段提供初始内存映射。
  2. 运行时作为所有进程共享的内核空间映射基准。
  3. 确保高效、一致的内核地址访问。

理解它对分析内存管理(如mm/目录代码)、进程切换及硬件交互(MMU/TLB)至关重要。