Linux内核分页机制详解:从理论到实践
linux-insides 项目地址: https://gitcode.com/gh_mirrors/li/linux-insides
分页机制概述
在现代操作系统中,分页(Paging)是内存管理的核心技术之一。它通过将线性地址转换为物理地址,实现了虚拟内存系统。Linux内核在x86_64架构下采用四级分页结构,这是理解内核内存管理的基础。
分页模式与启用条件
x86架构支持三种分页模式:
- 32位分页模式
- PAE(物理地址扩展)分页模式
- IA-32e分页模式(64位模式)
要启用IA-32e分页模式,需要设置三个关键位:
- CR0.PG位:启用分页机制
- CR4.PAE位:启用物理地址扩展
- IA32_EFER.LME位:启用长模式
在内核启动代码中,我们可以看到这些位的设置过程:
movl $(X86_CR0_PG | X86_CR0_PE), %eax # 设置PG和PE位
movl %eax, %cr0 # 写入CR0寄存器
movl $MSR_EFER, %ecx # 准备设置EFER寄存器
rdmsr # 读取EFER
btsl $_EFER_LME, %eax # 设置LME位
wrmsr # 写回EFER
四级分页结构详解
x86_64架构下的Linux内核使用四级分页结构:
- 页全局目录(Page Global Directory, PGD):顶级页表,CR3寄存器存储其物理地址
- 页上层目录(Page Upper Directory, PUD):第二级页表
- 页中间目录(Page Middle Directory, PMD):第三级页表
- 页表项(Page Table Entry, PTE):最底层页表,直接映射物理页帧
每个页表大小均为4KB,包含512个8字节的条目。地址转换过程如下:
- 48位虚拟地址被划分为多个部分,每部分作为索引查找下一级页表
- CR3寄存器提供PGD的基地址
- 依次通过PGD→PUD→PMD→PTE的查找过程
- 最终找到物理页帧,结合页内偏移得到完整物理地址
地址空间布局
x86_64架构理论上支持64位地址空间,但实际只使用48位地址,通过符号扩展形成64位地址。地址空间分为两大区域:
- 用户空间:0x0000000000000000到0x00007FFFFFFFFFFF
- 内核空间:0xFFFF800000000000到0xFFFFFFFFFFFFFFFF
两者之间的区域是非规范地址区域,访问会导致异常。内核空间又细分为多个区域:
- 直接物理内存映射区(ffff880000000000开始)
- vmalloc/ioremap区域
- 内核代码映射区(ffffffff80000000开始)
- 模块映射空间
- vsyscalls区域
页表条目结构
每个页表条目包含丰富的信息和控制位:
- N/X位:禁止执行位,控制页面是否可执行
- 物理地址字段:存储下一级页表或物理页的地址
- 访问控制位:
- U/S:用户/超级用户权限
- R/W:读写权限
- 缓存控制位:
- PWT:页级写通
- PCD:页级缓存禁用
- 状态位:
- A:访问位
- P:存在位
实际地址转换示例
以内核文本段地址0xFFFFFFFF81000000为例:
- 二进制表示:1111111111111111 111111111 111111110 000001000 000000000 000000000000
- 分解各部分:
- 47:39 → PGD索引
- 38:30 → PUD索引
- 29:21 → PMD索引
- 20:12 → PTE索引
- 11:0 → 页内偏移
通过四级页表逐级查找,最终映射到物理内存中的内核代码段。
总结
分页机制是Linux内核内存管理的核心,理解四级分页结构及其转换过程对于深入理解内核工作原理至关重要。本文从理论层面介绍了x86_64架构下的分页机制,包括地址空间布局、页表结构和地址转换过程。在后续内容中,我们将深入内核源码,分析分页结构的具体实现和初始化过程。
linux-insides 项目地址: https://gitcode.com/gh_mirrors/li/linux-insides
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考