- 程序内存布局
程序内存布局
os 中的每进程运行在属于它自己的内存沙盘中 (虚拟地址空间(virtual address space))
虚拟地址通过页表(page table)映射到物理内存,页表由 os 维护并被 cpu 引用
每个进程有一套属于它自己的页表,但一部分虚拟地址必须保留给内核使用
不意味着内核使用了那么多的物理内存,仅表示它可支配这么大地址空间,根据内核需要,映射到物理内存
- 内核空间常驻物理内存,并在所有进程中都映射到同样的物理内存
- 用户模式地址空间的映射随进程切换而不断变化
栈
最顶部
- 大多数编程语言用于存储 局部变量和函数参数
- 函数调用会将一个新的栈桢(stack frame)压入栈中
- 栈桢在函数返回时被清理
- 数据遵从 LIFO 顺序
简单设计意味着不必使用复杂的数据结构来追踪栈的内容,只要一个简单的指针指向栈的顶端即可;压栈(pushing)和出栈(popping)过程非常迅速、准确
持续的重用栈空间有助于使活跃的栈内存保持在 CPU 缓存中,从而加速访问。进程中的每一个线程都有属于自己的栈
页故障(page fault)
不断向栈中压入的数据,超出栈所对应内存区域将触发page fault,被 Linux 的 expand_stack() 处理,调用 acct_stack_growth() 检查是否还有合适的地方用于栈的增长
如果栈的大小低于 RLIMIT_STACK(通常是 8MB),一般情况下栈会被加长,程序继续愉快的运行,感觉不到发生了什么事情
如果达到了最大的栈空间大小,就会栈溢出(stack sverflow)
内存映射段
栈的下方
内核可将文件内容直接映射到内存
系统调用请求这种映射
- Linux 的 mmap()
- Windows 的 CreateFileMapping() / MapViewOfFile()
内存映射是一种方便高效的文件 I/O 方式,被用于加载动态库
创建不对应于任何文件的匿名内存映射用于存放程序的数据
Linux 通过 malloc() 请求一大块内存,C 运行库将会创建这样一个匿名映射而不是使用堆内存
大块意味着比 MMAP_THRESHOLD 还大,缺省是 128KB,可以通过 mallopt() 调整
堆
与栈一样,用于 运行时内存分配
但堆用于存储生存期与函数调用无关的数据
- C 是 malloc() 系列函数
- 有 gc 的语言(如 java)中,是 new 关键字
堆中有足够空间满足内存请求,就可被语言运行时库处理而不需要内核参与
否则,堆会被扩大,通过 brk() 系统调用来分配请求所需的内存块
BSS / 数据段
不映射到任何文件
static int cntActiveUsers 在 BSS
static int cntWorkerBees = 10 在数据段