1. 程序地址空间概述
在Linux系统编程中,程序地址空间是一个至关重要的概念。简单来说,它就像是操作系统为每个运行中的程序分配的一块"虚拟地盘"。这块地盘有多大?在32位系统上是4GB(2^32字节),64位系统上则大得惊人(2^64字节)。但这里有个有趣的矛盾点:你的物理内存可能只有8GB,为什么能同时运行多个4GB的程序呢?
我第一次真正理解这个概念是在调试一个内存泄漏的程序时。当时发现程序占用的虚拟内存(VIRT)不断增长,但实际使用的物理内存(RES)却保持稳定。这让我意识到,地址空间和物理内存之间存在着精妙的映射关系。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 虚拟地址空间布局
2.1 典型的内存布局
让我们用x86_64架构为例,看看典型的Linux进程地址空间布局:
code复制0x0000000000000000 - 0x00007fffffffffff (128TB) 用户空间
0x0000000000400000 - 代码段 (.text)
0x0000000000600000 - 数据段 (.data)
0x0000000000601000 - BSS段 (.bss)
0x00007ffffffde000 - 栈(stack)
0x00007ffff7ffb000 - 共享库映射
0x00007ffff7ffe000 - vDSO
0xffff800000000000 - 内核空间
注意:实际地址可能因ASLR(地址空间布局随机化)而有所不同。这是安全特性,我们后面会讨论。
2.2 各段详细解析
代码段(.text):存放编译后的机器指令。特点是只读、可执行。有趣的是,多个进程可以共享相同的.text段(比如运行同一个程序的多个实例)。
数据段(.data):存放已初始化的全局变量和静态变量。比如:
c复制int global_var = 42; // 存放在.data段
static int static_var = 100; // 同样在.data段
BSS段:存放未初始化的全局变量。BSS段有个特点:它在磁盘上不占空间,只在加载时由系统初始化为0。这优化了可执行文件的大小。
堆(heap):动态内存分配的区域,通过malloc/free管理。堆向高地址增长。
栈(stack):存放局部变量、函数参数等。栈向低地址增长。每个线程有自己的栈。
内存映射段:用于映射共享库、文件等。mmap系统调用就是操作这个区域。
3. 地址空间管理机制
3.1 页表与MMU
虚拟地址到物理地址的转换是通过页表(page table)完成的,由内存管理单元(MMU)硬件加速。现代系统通常使用4KB页大小(可通过`getconf P
