本页目录

进程内存布局

覆盖: /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 布局

64-bit 虚拟地址空间布局(48-bit 有效,128TB user + 128TB kernel)

0x0000_0000_0000_0000 NULL page(未映射,捕获 NULL 解引用)

0x0000_0000_0040_0000 PIE binary 段 .text(R+X)· .rodata · .plt/.got · .data · .bss

0x0000_0000_0060_0000 heap(brk/sbrk)↑ 向上增长

…… 大片未映射空当 ……

0x0000_7f00_0000_0000 mmap 区域(mmap base) 共享库(.so)· 匿名映射(malloc 大分配/线程栈)· 文件 mmap

0x0000_7fff_ffff_ffff [stack] 主线程栈 ↓ 向下增长

≈0x7fff_ffff_ffff [vvar] vDSO 数据页(kernel timekeeper,只读) [vdso] vDSO 代码页(clock_gettime 等,免 syscall) [vsyscall] 已废弃快速调用页(固定地址,安全风险,默认 emulate)

0xffff_ffff_ffff_ffff kernel space(用户态不可访问)

heap 从二进制段末尾向上长,mmap 区(含 stack)从高地址向下长—— 两者相向而行,中间巨大的未映射空当就是安全缓冲带。

/proc/pid/maps 详解

$ 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: 栈自动增长

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

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

Heap: brk vs mmap

// 传统的 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 → 内核模拟 → 返回)

工具

# 进程内存布局
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