下面是《【寻找Linux的奥秘】第七章:虚拟地址空间》的详细内容解读与结构化整理,适用于希望深入理解 Linux 虚拟内存管理机制的开发者与系统学习者。


📚 目录

  1. 什么是虚拟地址空间?
  2. 用户态与内核态的地址划分
  3. 虚拟内存区域(VMA)介绍
  4. Linux 进程地址空间布局
  5. 页表与地址映射机制
  6. 动态内存分配与映射方式
  7. 共享内存与匿名映射
  8. 虚拟地址空间与安全隔离
  9. Linux 中虚拟地址空间查看命令
  10. 总结与学习建议

1️⃣ 什么是虚拟地址空间?

虚拟地址空间是操作系统为每个进程提供的“独立”地址空间。每个进程都认为自己独享从 0 开始的地址空间,而实际上这些地址会映射到物理内存或交换空间。

本质:

  • 虚拟地址 ≠ 物理地址
  • 通过硬件(MMU)+ 软件(内核)协作完成映射

2️⃣ 用户态与内核态的地址划分

在 32 位 Linux 系统中,虚拟地址空间通常被划分为两部分:

区域地址范围权限描述
用户空间0x00000000 ~ 0xbfffffff用户可访问每个进程独立
内核空间0xc0000000 ~ 0xffffffff仅内核访问所有进程共享同一内核映射

在 64 位系统中,用户空间可扩展至 TB 级,具体取决于平台和内核编译选项。


3️⃣ 虚拟内存区域(VMA)介绍

VMA(Virtual Memory Area) 是 Linux 用来描述进程地址空间中一个连续区域的数据结构。

每个进程都维护一个 mm_struct 结构体,内部通过红黑树或链表管理多个 VMA。

每个 VMA 包含内容:

  • 起始地址与结束地址
  • 访问权限(读、写、执行)
  • 映射的文件(如 ELF、共享库)
  • 匿名页(malloc 或栈)

4️⃣ Linux 进程地址空间布局

一个典型的进程虚拟地址空间由以下几个区域组成:

低地址
|
|  text(代码段)
|  data(已初始化数据段)
|  bss(未初始化数据段)
|  heap(堆,向上增长)
|  mmap 区域(共享库、文件映射)
|  stack(栈,向下增长)
|  内核空间
高地址

5️⃣ 页表与地址映射机制

虚拟地址需通过页表转换为物理地址。Linux 使用分级页表,通常包括以下几级:

  • PGD(Page Global Directory)
  • PUD(Page Upper Directory)
  • PMD(Page Middle Directory)
  • PTE(Page Table Entry)

每次内存访问都通过这些表进行查找并转换。


6️⃣ 动态内存分配与映射方式

malloc / calloc:

  • 调用 brk() 或 mmap() 系统调用
  • 在虚拟地址空间的 heap 区域或匿名映射区域分配

文件映射(mmap):

  • 使用 mmap() 映射文件或设备
  • 可选择是否共享、是否匿名、读写权限等

7️⃣ 共享内存与匿名映射

  • mmap() 可实现内存共享(如线程间共享内存)
  • 匿名映射(MAP_ANONYMOUS)用于无文件 backing 的内存,如线程栈、临时缓冲区

多个进程或线程可通过 mmap + MAP_SHARED 来映射同一段物理内存区域。


8️⃣ 虚拟地址空间与安全隔离

虚拟地址空间为每个进程提供“沙箱”式运行环境:

  • 防止进程互相访问彼此内存
  • 内核空间与用户空间分离,避免用户代码直接操作内核内存
  • 若访问非法地址,触发 segfault(段错误)

9️⃣ Linux 中虚拟地址空间查看命令

查看进程的地址空间布局:

cat /proc/<pid>/maps

示例输出:

00400000-0040b000 r-xp 00000000 fd:00 123456 /usr/bin/ls
0060a000-0060b000 r--p 0000a000 fd:00 123456 /usr/bin/ls
...
7fffe12b000-7fffe14c000 rw-p 00000000 00:00 0 [stack]

每一行表示一个 VMA 区段,包括:

  • 起始和结束地址
  • 权限(rwx)
  • 映射文件或 [heap]/[stack]/[vdso]

查看某地址所属页表项:

sudo cat /proc/<pid>/pagemap

或使用专用工具:

pmap <pid>

🔟 总结与学习建议

  • 虚拟地址空间是进程隔离的关键保障
  • 每个进程拥有独立 VMA 结构描述其地址区域
  • mmap() 是内存映射与文件共享的强大工具
  • 掌握 /proc/<pid>/maps 输出有助于调试段错误
  • 进一步建议阅读 Linux 源码中的 mm 子系统代码,如 do_mmapvm_area_struct 等

🔗 参考资料(出站链接)