---
title: 进程内存布局
url: https://doc.liz6.com/systems-programming/03-processes-and-memory/01-process-memory-layout
locale: zh
area: systems-programming
tags:
- systems-programming
- 进程与内存
date: 2026-06-30
modified: 2026-07-11
description: '覆盖: /proc/pid/maps 解读 → stack/heap/mmap/vDSO/vvar/vsyscall → segments (text/data/bss) → 内存布局与 ASLR → mmap 映射类型 适用: Linux x86-64/ARM64 用户态'
---

# 进程内存布局

> 覆盖: /proc/pid/maps 解读 → stack/heap/mmap/vDSO/vvar/vsyscall → segments (text/data/bss) → 内存布局与 ASLR → mmap 映射类型
> 适用: Linux x86-64/ARM64 用户态

## 概述

每个 Linux 进程看到的是 48-bit（或 57-bit）的虚拟地址空间。理解这个空间的布局对调试内存越界、分析 core dump、优化内存使用都至关重要。`/proc/pid/maps` 是这个布局的"地图"。

## 典型 64-bit 布局

<svg viewBox="0 0 720 415" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="64-bit 虚拟地址空间从低到高的布局:NULL页、二进制段、堆、mmap区、栈、vDSO/vvar/vsyscall、内核空间">
  <defs>
    <marker id="memlayout-ah" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#475569"/>
    </marker>
  </defs>
  <rect width="720" height="415" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">64-bit 虚拟地址空间布局(48-bit 有效,128TB user + 128TB kernel)</text>

  <!-- growth direction guide -->
  <text x="18" y="56" font-size="10" fill="#64748b">低</text>
  <line x1="30" y1="50" x2="30" y2="326" stroke="#475569" stroke-width="1.6" marker-end="url(#memlayout-ah)"/>
  <text x="14" y="322" font-size="10" fill="#64748b">高</text>

  <!-- 1. NULL page -->
  <text x="145" y="65" text-anchor="end" font-size="9.5" fill="#64748b">0x0000_0000_0000_0000</text>
  <rect x="150" y="50" width="500" height="22" rx="6" fill="#e2e8f0" stroke="#cbd5e1"/>
  <text x="400" y="65" text-anchor="middle" font-size="11" fill="#475569">NULL page(未映射,捕获 NULL 解引用)</text>

  <!-- 2. binary segments -->
  <text x="145" y="88" text-anchor="end" font-size="9.5" fill="#64748b">0x0000_0000_0040_0000</text>
  <rect x="150" y="76" width="500" height="46" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="400" y="94" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">PIE binary 段</text>
  <text x="400" y="110" text-anchor="middle" font-size="10.5" fill="#4f46e5">.text(R+X)· .rodata · .plt/.got · .data · .bss</text>

  <!-- 3. heap -->
  <text x="145" y="142" text-anchor="end" font-size="9.5" fill="#64748b">0x0000_0000_0060_0000</text>
  <rect x="150" y="126" width="500" height="26" rx="6" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="400" y="143" text-anchor="middle" font-size="11" font-weight="600" fill="#0f766e">heap(brk/sbrk)↑ 向上增长</text>

  <!-- 4. gap -->
  <rect x="150" y="156" width="500" height="20" rx="4" fill="none" stroke="#cbd5e1" stroke-dasharray="4 3"/>
  <text x="400" y="170" text-anchor="middle" font-size="10" fill="#94a3b8">…… 大片未映射空当 ……</text>

  <!-- 5. mmap region -->
  <text x="145" y="192" text-anchor="end" font-size="9.5" fill="#64748b">0x0000_7f00_0000_0000</text>
  <rect x="150" y="180" width="500" height="46" rx="8" fill="#e0e7ff" stroke="#a5b4fc"/>
  <text x="400" y="198" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">mmap 区域(mmap base)</text>
  <text x="400" y="214" text-anchor="middle" font-size="10" fill="#4338ca">共享库(.so)· 匿名映射(malloc 大分配/线程栈)· 文件 mmap</text>

  <!-- 6. stack -->
  <text x="145" y="246" text-anchor="end" font-size="9.5" fill="#64748b">0x0000_7fff_ffff_ffff</text>
  <rect x="150" y="230" width="500" height="26" rx="6" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="400" y="247" text-anchor="middle" font-size="11" font-weight="600" fill="#0f766e">[stack] 主线程栈 ↓ 向下增长</text>

  <!-- 7. vvar/vdso/vsyscall -->
  <text x="145" y="278" text-anchor="end" font-size="9.5" fill="#64748b">≈0x7fff_ffff_ffff</text>
  <rect x="150" y="260" width="500" height="44" rx="8" fill="#ffedd5" stroke="#fdba74"/>
  <text x="400" y="274" text-anchor="middle" font-size="10.3" fill="#9a3412">[vvar] vDSO 数据页(kernel timekeeper,只读)</text>
  <text x="400" y="287" text-anchor="middle" font-size="10.3" fill="#9a3412">[vdso] vDSO 代码页(clock_gettime 等,免 syscall)</text>
  <text x="400" y="300" text-anchor="middle" font-size="10.3" fill="#9a3412">[vsyscall] 已废弃快速调用页(固定地址,安全风险,默认 emulate)</text>

  <!-- 8. kernel space -->
  <text x="145" y="322" text-anchor="end" font-size="9.5" fill="#64748b">0xffff_ffff_ffff_ffff</text>
  <rect x="150" y="308" width="500" height="24" rx="6" fill="#fef2f2" stroke="#ef4444"/>
  <text x="400" y="324" text-anchor="middle" font-size="11" font-weight="700" fill="#dc2626">kernel space(用户态不可访问)</text>

  <!-- insight -->
  <rect x="60" y="348" width="600" height="52" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="368" font-size="12.5" fill="#3730a3">heap 从二进制段末尾向上长,mmap 区(含 stack)从高地址向下长——</text>
  <text x="76" y="388" font-size="12.5" fill="#3730a3">两者相向而行,中间巨大的未映射空当就是安全缓冲带。</text>
</svg>

## /proc/pid/maps 详解

```bash
$ cat /proc/self/maps
# 格式: address perms offset dev inode pathname

# === Binary (PIE) ===
55a8e3c00000-55a8e3c01000 r--p 00000000 08:02 1234567 /bin/sleep
55a8e3c01000-55a8e3c03000 r-xp 00001000 08:02 1234567 /bin/sleep  ← .text (R+X)
55a8e3c03000-55a8e3c04000 r--p 00003000 08:02 1234567 /bin/sleep  ← .rodata
55a8e3c04000-55a8e3c05000 r--p 00003000 08:02 1234567 /bin/sleep
55a8e3c05000-55a8e3c06000 rw-p 00004000 08:02 1234567 /bin/sleep  ← .data+.bss

# === Heap ===
55a8e4c00000-55a8e4c21000 rw-p 00000000 00:00 0       [heap]

# === Shared libraries ===
7f1234000000-7f1234001000 r--p 00000000 08:02 9999 /lib64/ld-linux-x86-64.so.2
7f1234001000-7f123402a000 r-xp 00001000 08:02 9999 /lib64/ld-linux-x86-64.so.2
7f123402a000-7f1234034000 r--p 0002a000 08:02 9999 /lib64/ld-linux-x86-64.so.2
7f1234035000-7f1234037000 rw-p 00034000 08:02 9999 /lib64/ld-linux-x86-64.so.2

# === Thread stack (匿名 mmap) ===
7f1233e00000-7f1234000000 rw-p 00000000 00:00 0       ← 线程栈

# === Stack ===
7ffc12345000-7ffc12366000 rw-p 00000000 00:00 0       [stack]
7ffc123de000-7ffc123e2000 r--p 00000000 00:00 0       [vvar]
7ffc123e2000-7ffc123e4000 r-xp 00000000 00:00 0       [vdso]
```

### 权限位解读

```
r--p: read-only, private              ← .rodata, read-only segments
r-xp: read+execute, private           ← .text
rw-p: read+write, private             ← .data, .bss, heap, stack
r--s: read-only, shared               ← rarely used
rw-s: read+write, shared              ← MAP_SHARED mappings
---p: no access, private              ← guard pages (stack/heap guard)
```

### 匿名映射 vs 文件映射

```
[heap], [stack], [vdso]:              匿名映射 (无文件名, kernel 创建)
/lib64/libc.so.6:                     文件映射 (有文件名, mmap from file)
7f1233e00000 (无 [name]):             匿名映射 (malloc, thread stack)
                                        → 不是 [heap]! malloc 用 mmap
```

---

## 各区域详解

### Stack: 栈自动增长

```c
// 栈使用 mmap 或 brk 分配 (取决于内核配置)
// VM_GROWSDOWN: push 超出当前栈底 → page fault → 内核自动扩展栈
//   → 扩展受 RLIMIT_STACK (默认 8MB) 限制
//   → 栈顶下方有 guard page (---p) 防止越界

// 主线程栈: 位于 [stack]
// 子线程栈: pthread_create → mmap (通常 8MB, 位置在 mmap 区域)
```

### Heap: brk vs mmap

```c
// 传统的 brk/sbrk: 扩展 data segment (直接跟在 .bss 后面)
//   → 用户态: brk(addr) → 改变 program break
//   → 问题: 碎片化, 不能独立释放

// malloc 现在的策略:
//   1. 小分配 (< 128KB default): 用 brk → 放在 [heap] 区域
//   2. 大分配 (>= 128KB):  用 mmap → 独立的匿名映射
//   3. mmap threshold: 可通过 mallopt(M_MMAP_THRESHOLD) 调整
```

### vDSO / vvar / vsyscall: 内核注入

```
[vdso]:   内核在每个进程地址空间注入的 .so
          提供: __vdso_clock_gettime, __vdso_getcpu, __vdso_time, __vdso_getrandom
          → 用户态执行, 不 syscall → 极快

[vvar]:   vDSO 的数据页 (内核 timekeeper 结构, per-CPU 数据)
          → R/O for userland

[vsyscall]: 已废弃的快速系统调用页面 (x86 only)
            → 固定地址: 0xffffffffff600000 (安全风险)
            → 现在默认 emulate (page fault → 内核模拟 → 返回)
```

---

## 工具

```bash
# 进程内存布局
cat /proc/self/maps
cat /proc/self/smaps | head -50        # 每 mapping 的详细统计 (RSS/PSS/swap)

# 内存映射汇总
pmap -x <pid>

# 内存使用概要
cat /proc/self/status | grep Vm
# VmPeak: 历史最大虚拟内存
# VmSize: 当前虚拟内存
# VmRSS:  物理内存 (Resident)
# VmData/VmStk/VmExe/VmLib: 数据/栈/代码/库

# ASLR 设置
cat /proc/sys/kernel/randomize_va_space  # 0=off, 1=partial, 2=full (default)
```

## 参考

- **内核文档**: `Documentation/filesystems/proc.rst` (maps/smaps)
- **源码**: `fs/proc/task_mmu.c` (/proc/pid/maps 实现), `arch/x86/entry/vdso/`
- **LWN**: "The vDSO and vvar", "ASLR for 64-bit Linux"

*关键词: /proc/pid/maps, stack, heap, vDSO, vvar, ASLR, PIE, smaps, brk, mmap*
