2. 内存管理基础:物理内存与虚拟内存、地址空间概念、MMU硬件基础

内存管理,说白了就是操作系统怎么管好这块“临时仓库”。你写代码时觉得内存无限大,那是错觉。真实情况是:物理内存就那么点,程序却想占很多。怎么协调?靠的就是虚拟内存和MMU这套组合拳。

我刚开始接触这部分时,也觉得抽象。直到有一次在嵌入式项目里,物理内存只有64MB,却要跑三个进程,其中一个还偷偷申请了200MB……嗯,从那以后,我彻底理解了虚拟内存存在的意义。

2.1 物理内存 vs 虚拟内存

先搞清楚两个概念:

  • 物理内存:就是插在主板上的那根内存条。CPU通过地址总线直接访问它。大小固定,比如4GB、8GB。
  • 虚拟内存:操作系统给每个进程画的“大饼”。每个进程都以为自己独占整个地址空间(比如32位下4GB)。实际上,这块“大饼”可能只有一小块在物理内存里,其余在磁盘上。

为什么要这么折腾?直接让程序用物理地址不行吗?

不行。原因有三:

  1. 隔离性:进程A不能乱写进程B的内存。虚拟地址让每个进程有自己的“独立世界”。
  2. 安全性:恶意程序不能直接访问内核空间。
  3. 高效利用:物理内存不够时,把不常用的数据换到磁盘,腾出空间给活跃进程。

核心观点:虚拟内存不是“假内存”,而是一种地址转换机制。它让每个进程觉得“我有整个内存”,实际上操作系统在背后偷偷做调度。

2.2 地址空间概念

地址空间,就是CPU能访问的所有地址集合。分两种:

类型 范围 谁在用 特点
物理地址空间 0 ~ 物理内存上限 硬件、内核 真实存在,大小固定
虚拟地址空间 0 ~ 2^n-1(n是地址位数) 用户进程 每个进程独立,大小由CPU决定

32位系统下,虚拟地址空间是4GB。Linux通常把高1GB留给内核(0xC0000000 ~ 0xFFFFFFFF),低3GB给用户进程。Windows类似,但分界线不同。

我记得有一次调试一个内存泄漏问题,进程申请了2.8GB虚拟内存后突然崩溃。查了半天,发现是用户空间只剩200MB了,而内核空间不能碰。嗯,这就是地址空间划分的“硬边界”。

避坑指南:我曾经以为虚拟地址空间越大越好。其实不然。64位系统下虚拟地址空间巨大(16EB),但物理内存有限。过度申请虚拟内存会导致频繁缺页中断,性能急剧下降。

2.3 MMU硬件基础

MMU(Memory Management Unit,内存管理单元)是CPU内部的一个硬件模块。它的核心工作就一件:把虚拟地址翻译成物理地址。

翻译过程大致如下:

  1. CPU发出虚拟地址(比如0x00401000)。
  2. MMU截获这个地址,查页表(Page Table)。
  3. 页表里记录了虚拟页号对应的物理页框号。
  4. 如果命中,直接得到物理地址,访问内存。
  5. 如果没命中(缺页),触发缺页异常,操作系统介入。

整个过程对用户进程完全透明。你写代码时根本感觉不到MMU的存在——这才是它的高明之处。

注意:MMU不是万能的。页表本身也占内存。一个32位进程的页表大约需要4MB空间(按每页4KB计算)。如果系统有100个进程,光页表就要400MB。所以现代CPU引入了多级页表和TLB(快表)来优化。

2.4 地址转换流程(SVG图)

下面这张图展示了从虚拟地址到物理地址的完整转换路径:

虚拟地址 → 物理地址转换流程 虚拟地址 MMU 页表查询 物理地址 缺页异常 说明: 1. 虚拟地址由CPU发出,MMU截获后查页表。 2. 页表命中 → 直接得到物理地址,访问内存。 3. 页表未命中 → 触发缺页异常,操作系统从磁盘加载数据。

2.5 页表与TLB

页表是MMU工作的核心数据结构。每个进程都有自己的页表,记录了虚拟页号到物理页框号的映射关系。

但页表有个问题:它存在内存里。每次地址转换都要查内存,太慢了。所以CPU引入了TLB(Translation Lookaside Buffer,快表),一个硬件缓存,专门存最近用过的页表项。

TLB的命中率通常很高(99%以上)。一旦命中,地址转换只需1个CPU周期。没命中,才去查内存里的页表,耗时几十个周期。

优化技巧:我曾在性能调优时发现,某个进程的TLB缺失率异常高。查下来是因为它频繁访问大量分散的内存页。解决方案是改用大页(Huge Pages),减少页表项数量,TLB覆盖范围更大。效果立竿见影。

2.6 多级页表

32位系统下,如果只用一级页表,每个进程需要约4MB空间存页表。100个进程就是400MB。这还不算多,但64位系统下,一级页表会大到无法接受。

解决方案是多级页表。以x86-64为例,使用4级页表:

  • PML4(Page Map Level 4)
  • PDPT(Page Directory Pointer Table)
  • PD(Page Directory)
  • PT(Page Table)

虚拟地址被拆成5部分:4个索引 + 1个偏移量。MMU逐级查表,最终找到物理页框。

好处是:大部分页表项是空的,不需要分配物理内存。只有真正用到的地址范围才建立页表。这样既节省内存,又支持大地址空间。

注意:多级页表虽然省内存,但增加了地址转换的耗时。每次查表都要访问内存4次(4级页表)。加上TLB缺失时的开销,性能影响不可忽视。所以TLB的命中率至关重要。

2.7 本章小结

物理内存是硬件资源,虚拟内存是操作系统提供的抽象。MMU负责把虚拟地址翻译成物理地址,页表是翻译的“字典”,TLB是字典的“缓存”。

理解这套机制,你才能明白:为什么进程能申请比物理内存还大的空间?为什么两个进程的相同虚拟地址不会冲突?为什么内存访问有时快有时慢?

嗯,这些问题的答案,都在本章里了。