graph LR
A["进程视角"] -->|"看到"| B["虚拟地址 0x0000-0xFFFF..."]
C["物理内存"] -->|"真实"| D["物理页帧 0-N"]
B -.->|"MMU 翻译"| D
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
graph TB
A["进程虚拟地址空间"] --> B["VMA 1: 代码段\n0x400000-0x500000"]
A --> C["VMA 2: 数据段\n0x600000-0x700000"]
A --> D["VMA 3: 堆\n0x800000-0x900000"]
A --> E["VMA 4: mmap 区\n0xA00000-0xB00000"]
A --> F["VMA 5: 栈\n0x7FFFFFFF...向下"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333
graph TB
A["64 位虚拟地址空间"] --> B["用户空间\n0 - 0x00007FFFFFFFFFFF\n128 TB"]
A --> C["内核空间\n0xFFFF800000000000 -\n128 TB"]
B --> B1["栈 (向下)"]
B --> B2["mmap 区"]
B --> B3["堆 (向上)"]
B --> B4["BSS"]
B --> B5["数据段"]
B --> B6["代码段"]
C --> C1["直接映射区\n物理内存"]
C --> C2["vmalloc 区"]
C --> C3["临时映射"]
C --> C4["固定映射"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style B1 fill:#FFF9C4,stroke:#F9A825,color:#333
style B2 fill:#FFF9C4,stroke:#F9A825,color:#333
style B3 fill:#FFF9C4,stroke:#F9A825,color:#333
style B4 fill:#FFF9C4,stroke:#F9A825,color:#333
style B5 fill:#FFF9C4,stroke:#F9A825,color:#333
style B6 fill:#FFF9C4,stroke:#F9A825,color:#333
style C1 fill:#FFF9C4,stroke:#F9A825,color:#333
style C2 fill:#FFF9C4,stroke:#F9A825,color:#333
style C3 fill:#FFF9C4,stroke:#F9A825,color:#333
style C4 fill:#FFF9C4,stroke:#F9A825,color:#333
2.3 各区域作用
区域
起始
增长方向
内容
代码段
0x08048000
-
程序代码、.text
数据段
-
-
初始化数据 .data
BSS
-
-
未初始化数据 .bss
堆
start_brk
向上
malloc/new 分配
mmap
共享库起点
向下
动态库、mmap 文件
栈
高地址
向下
局部变量、调用栈
内核
0xC0000000
-
内核代码 + 数据
三、写时复制(Copy-on-Write, COW)
3.1 什么是 COW?
graph TB
A["fork() 调用"] --> B["父子进程共享同一物理页\n标记为只读"]
B --> C{"任一方尝试写入?"}
C -->|"是"| D["触发缺页中断"]
D --> E["分配新物理页"]
E --> F["复制原页内容"]
F --> G["修改进程页表"]
G --> H["重试写入"]
C -->|"否"| I["继续共享"]
I --> J["物理页只读"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#E8D5F5,stroke:#CE93D8,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#B5EAD7,stroke:#80CBC4,color:#333
style G fill:#C7CEEA,stroke:#9FA8DA,color:#333
style H fill:#B5EAD7,stroke:#80CBC4,color:#333
style I fill:#C7CEEA,stroke:#9FA8DA,color:#333
style J fill:#C7CEEA,stroke:#9FA8DA,color:#333
graph TB
A["mmap 用途"] --> B["1. 文件映射\n文件 → 内存"]
A --> C["2. 匿名映射\n私有内存"]
A --> D["3. 共享内存\n进程间通信"]
A --> E["4. 动态库加载\n动态链接"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:`#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
voidfunc() { int x = 1; // 栈分配 char buf[1024]; // 栈分配 // 函数返回时自动释放 }
栈帧结构:
graph TB
A["栈帧"] --> B["局部变量"]
A --> C["保存的寄存器"]
A --> D["返回地址"]
A --> E["上一个栈帧指针\nrbp"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
getrusage(RUSAGE_SELF, &usage); printf("After allocation:\n"); printf(" Minor faults: %ld\n", usage.ru_minflt); printf(" Major faults: %ld\n", usage.ru_majflt);
free(p); return0; }
十、回到 5 个核心要点
graph TB
A["进程地址空间核心"] --> B["mm_struct\n总控结构"]
A --> C["vm_area_struct\n区域描述"]
A --> D["COW\n写时复制"]
A --> E["mmap\n内存映射"]
A --> F["缺页中断\n按需分页"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333