一 概述
二 类型
三 语句
四 函数
五 数据
六 内存
七 代码
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
8. 内存布局
进程内存布局与编码息息相关的是堆(heap)和栈(stack)。
- stack : 用于维护函数调用上下文。
- heap : 动态分配内存区。
进程内存模型示意图
| | 0x400000 +--------------+ | .init, .text | +--------------+ | .rodata | +--------------+ | .data, .bss | +--------------+ | heap | v +--------------+ | ... | mmap +--------------+ | dynlib | +--------------+ | ... | +--------------+ | stack | ^ 0x7ffffffff000 +--------------+ | vsyscall |
栈
栈由高位向低位扩展,通常以 SP 寄存器定位栈顶。每个函数调用,都会在栈划出一个区域,称做栈帧(stack frame),以 [esp, ebp]
确定范围,BP 也被称做帧指针(frame pointer)。
默认栈大小受系统限制,超出会导致段错误。使用
ulimit -s
查看或修改。
栈帧保存如下内容:
- 上下文。(如寄存器数据,用于结束时恢复现场)
- 当前函数局部变量。(非静态)
- 调用其他函数是的参数。(register, cdecl)
SP +-------------+--------- | ... | +-------------+ | local | +-------------+ | context | BP +-------------+--------- | old BP | 调用方帧指针 +-------------+ | PC | 调用方返回地址 +-------------+
#include <stdio.h> #include <stdlib.h> int add(int x, int y) { int z = x + y; return z; } int main (int argc, char *argv[]) { int x = 0x11; int y = 0x22; int z = add(x, y); printf("%d\n", z); return EXIT_SUCCESS; }
$ objdump -d -M intel ./test | less -p "<main>:"
参数
-fomit-frame-pointer
可以取消 BP 寄存器使用,但不推荐。指令 leave =pop sp, bp; pop bp
,清除当前栈帧,然后 ret 再 pop 恢复 PC/IP 回到调用方。
堆
目前 Linux 标准发行版使用 glibc/ptmalloc2 堆分配器。
- 向操作系统申请大块内存,交由分配器管理。
- 短生命周期使用 sbrk。
- 分配和释放时,合并小块内存。
- 释放时可能被放回内存池。
- 长生命周期大内存使用 mmap。
- 特大块内存申请使用 mmap。
google tcmalloc, freebsd jemalloc
#include <stdio.h> #include <stdlib.h> #include <sys/mman.h> int main (int argc, char *argv[]) { char *p = malloc(1024); *p = 0x22; char *p2 = mmap(NULL, 1<<20, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); *p2 = 0x33; printf("%p, %p\n", p, p2); getchar(); free(p); munmap(p2, 1<<20); return EXIT_SUCCESS; }
$ gcc -g -O0 -Wall -std=gnu11 -o test test.c # 使用 gnu,否则 MAP_ANONYMOUS 未定义。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论