当前位置:首页>Linux>【深度剖析】: Linux内核之内存寻址

【深度剖析】: Linux内核之内存寻址

  • 2026-10-22 18:46:18
【深度剖析】: Linux内核之内存寻址
Hello,大家好,我是程序媛MM。

本文约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 系统对内存的高效管理。

以上为全文内容。

往期文章(欢迎订阅技术分享栏目全部文章):

【项目实战】嵌入式软件系统中Flash进入硬件写保护的问题定位以及用软件操作解除写锁定的实现方法
【项目实战】保姆级教程: PWM控制白光灯及亮度调节
【项目实战】Linux下开通虚拟网桥功能的方法和遇到的问题及解决方案
【项目实战】Linux下音频对讲功能延时卡顿障故排查与解决
【项目实战】SPI Nor Flash下OTA升级概率失败导致系统成砖问题的排查和解决方法

“谢谢你看到这里”

这里是女程序员的笔记本

 15年+嵌入式软件工程师兼二胎宝妈

分享读书心得、工作经验,自我成长和生活方式。

希望我的文字能对你有所帮助

最新文章

随机文章