本文约2500字,今天读《深入Linux内核架构》第二章之内存寻址,这本书我是打乱顺序来读的。本文整理了内核寻址的核心学习笔记。
关注公众号, 即可获得与Linux相关的电子书籍(含《深入Linux内核架构》)以及常用开发工具,文末有文档清单。

一 章节核心目标
内存寻址是内核与硬件交互的核心环节,其核心目标是建立虚拟地址与物理地址的映射关系,实现三大功能:
地址隔离:不同进程的虚拟地址空间相互独立,避免非法访问。
内存扩展:通过虚拟内存突破物理内存容量限制。
高效访问:结合硬件 MMU(内存管理单元)和内核页表机制,实现地址快速转换。
二 物理内存与虚拟内存基础
[1]. 物理内存(Physical Memory)
定义:CPU 可直接访问的实际内存(RAM),以字节为基本存储单位,物理地址是硬件层面的绝对地址。
局限性:
>>容量有限,受硬件配置限制;
>>若直接使用物理地址,进程间地址冲突,无法实现隔离。
[2]. 虚拟内存(Virtual Memory)
定义:内核为进程提供的抽象地址空间,进程所有内存操作均基于虚拟地址,与物理内存解耦。
核心优势:
>>进程拥有统一的虚拟地址空间布局,与物理内存分布无关;
>>支持按需分配:仅当进程访问虚拟地址时,才分配对应的物理内存页帧。
[3]. 地址转换的必要性
虚拟地址无法直接被硬件识别,必须通过MMU + 页表转换为物理地址,才能访问实际内存。
地址转换是 ** 硬件(MMU)+ 软件(内核页表)** 协同完成的过程。
三 x86 架构的寻址模式(硬件基础)
Linux 内存寻址依赖 CPU 架构的寻址模式,以 x86 架构为例,主要经历 3 个阶段,决定了地址转换的方式。

关键补充:
分段:将虚拟地址划分为段选择子 + 段内偏移,先转换为线性地址(32 位)。x86 保护模式下必须启用分段,但 Linux 对其进行了弱化处理(所有进程使用相同的段描述符,段基址为 0,段内偏移 = 线性地址),本质上是 “平坦地址空间”。
分页:将线性地址划分为页目录 + 页表 + 页内偏移,转换为物理地址。这是 Linux 内存寻址的核心机制。

四 虚拟地址空间的划分(32 位/64 位)
[1]. 32 位 Linux 系统(虚拟地址空间 4GB)
内核将 4GB 虚拟地址空间分为两部分,用户态与内核态严格隔离:
用户态空间:0x00000000 ~ 0xBFFFFFFF(低 3GB)
每个进程独占,包括代码段、数据段、堆、栈、共享库等;
进程退出后,该空间被释放。
内核态空间:0xC0000000 ~ 0xFFFFFFFF(高 1GB)
所有进程共享,存放内核代码、数据、页表、物理内存映射等;
用户态进程无法直接访问,需通过系统调用陷入内核态。
32 位内核空间的细分(!!!关键)
为解决 1GB 内核空间不足以映射全部物理内存的问题,内核将高 1GB 空间分为 3 部分:

高端内存的意义:32 位系统中,若物理内存 > 896MB,超出部分无法直接映射到内核空间,需通过高端内存的动态映射机制临时访问,避免内存浪费。
[2]. 64 位 Linux 系统(以 x86_64 为例)
虚拟地址长度:硬件支持 48 位有效地址,对应256TB虚拟地址空间。
空间划分:默认采用1:1划分,用户态和内核态各占 128TB:
用户态:0x0000000000000000 ~ 0x00007FFFFFFFFFFF
内核态:0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF
优势:内核空间足够大,无需划分高端内存,所有物理内存均可直接映射,大幅简化寻址逻辑。
五 地址转换的核心机制:多级页表
[1]. 单级页表的缺陷
若采用单级页表(虚拟地址直接对应页表项),32 位系统需1024*1024个页表项(每个页表项 4 字节),占用4MB内存,且每个进程都需独立页表,内存开销极大。
[2]. 多级页表的设计思路
将虚拟地址划分为多个层级的索引,仅为实际使用的虚拟地址分配页表项,大幅减少页表占用的内存空间。
[3]. x86 架构的多级页表实现
>>32 位系统:二级页表
虚拟地址(32 位)划分为 3 部分:
页目录索引(10 位)→ 页目录项(PDE)
页表索引(10 位)→ 页表项(PTE)
页内偏移(12 位)→ 对应物理页帧内的字节位置(4KB 页大小,2^12=4096)
>>转换流程:
CPU 将页目录基址寄存器(CR3)的值作为页目录的物理地址;
通过页目录索引找到对应的 PDE,判断该页表是否存在;
若存在,通过页表索引找到对应的 PTE,获取物理页帧号;
物理页帧号 + 页内偏移 = 最终物理地址。
>>64 位系统:四级页表(x86_64)
虚拟地址(48 位)划分为 5 部分:
页全局目录索引(PGD,9 位)
页上级目录索引(PUD,9 位)
页中间目录索引(PMD,9 位)
页表索引(PTE,9 位)
页内偏移(12 位)
核心优化:支持大页(如 2MB、1GB),大页可跳过部分页表层级,减少地址转换的开销,适合嵌入式、数据库等对内存访问速度要求高的场景。
[4]. 关键硬件组件
MMU:内存管理单元,集成在 CPU 内部,负责执行地址转换的硬件逻辑;
TLB(快表):MMU 中的高速缓存,存储最近使用的页表项,避免每次访问内存都要查询页表(页表存储在内存中,访问速度慢),大幅提升地址转换效率;
CR3 寄存器:存储当前进程页目录的物理地址,进程切换时,内核会更新 CR3 的值,实现进程地址空间的切换。
六 内核态的特殊寻址机制
[1]. 物理内存的直接映射(低端内存)
32 位系统中,低端内存(前 896MB)的虚拟地址与物理地址的映射关系为:
虚拟地址 = 物理地址 + 0xC0000000(PAGE_OFFSET)
这种映射是永久的,内核可直接通过虚拟地址访问物理内存,无需额外转换;
适用于内核频繁访问的物理内存区域(如内核代码、数据、设备缓冲区)。
[2]. 高端内存的动态映射
32 位系统中,超过 896MB 的物理内存属于高端内存,无法直接映射,内核通过 3 种方式临时访问:

七 核心考点与易错点总结
[1].分段机制在 Linux 中的作用:x86 保护模式下必须启用分段,但 Linux 将所有段的基址设为 0,段内偏移等于线性地址,本质上是 “平坦地址空间”,分页才是 Linux 寻址的核心。
[2].多级页表的优势:减少页表的内存开销,支持非连续的物理内存映射。
[3].TLB 的作用:缓存页表项,提升地址转换效率,进程切换会导致 TLB 失效(需刷新 TLB)。
[4].32 位 vs64 位寻址的区别:64 位系统无需高端内存机制,支持更大的虚拟地址空间和大页,寻址逻辑更简单。
[5].CR3 寄存器的意义:存储当前进程页目录的物理地址,是进程地址空间隔离的关键。
八 与内存管理的关联
内存寻址是内存管理的基础:
内存寻址解决 “虚拟地址如何转换为物理地址” 的问题;
内存管理解决 “物理页帧如何分配、回收、复用” 的问题;
两者结合,共同实现 Linux 系统对内存的高效管理。
以上为全文内容。
这里是女程序员的笔记本
15年+嵌入式软件工程师兼二胎宝妈
分享读书心得、工作经验,自我成长和生活方式。
希望我的文字能对你有所帮助