下图是x86_64下linux进程的默认内存布局形式:
下面逐一分析以上各个地址段的含义。
text 代码段
代码段,从虚拟内存地址00400000开始,使用pmap $$可以查看到,这个地址是固定的:
- linux # pmap $$
- 27729: bash
- START SIZE RSS PSS DIRTY SWAP PERM MAPPING
- 00400000 552K 480K 260K 0K 0K r-xp /bin/bash
- 00689000 4K 4K 4K 4K 0K r--p /bin/bash
- 0068a000 20K 20K 20K 20K 0K rw-p /bin/bash
- 0068f000 1420K 1356K 1356K 1356K 0K rw-p [heap]
- 7f4f47b72000 64K 4K 4K 0K 0K r-xp /usr/lib64/gconv/libGB.so
- ……
- 7f4f48c33000 4K 4K 4K 4K 0K rw-p [anon]
- 7fff0f0f1000 84K 20K 20K 20K 0K rw-p [stack]
- 7fff0f1ff000 4K 4K 0K 0K 0K r-xp [vdso]
- ffffffffff600000 4K 0K 0K 0K 0K r-xp [vsyscall]
- Total: 18944K 3256K 2225K 1580K 0K
- 1652K writable-private, 17052K readonly-private, 240K shared, and 3256K referenced
00400000地址段用于存放程序的可执行文件,fork一个子进程,之后调用execve加载可执行文件,execve即将文件加载到该段地址中:
- linux # strace ls
- execve("/bin/ls", ["ls"], [/* 65 vars */]) = 0
- brk(0) =0x618000
- mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) = 0x7f0422e2a000
- ……
data 数据段
数据段用于存放已初始化全局变量。
bss段
该段用于存放全局的、但未初始化的变量。bss段与data段的区别是,编译时需为data段分配空间,而bss段不用。数据存放于bss段,减小了可执行程序的文件大小。
heap
堆,c库中的malloc调用就是从该段获取内存空间。
memory map
mmap系统调用可将文件映射入进程memory map地址空间。使用pmap $$查看,[heap]下面部分就是mmap区域,对于以上"pmap $$"输出:
- 7f4f47b72000 64K 4K 4K 0K 0K r-xp /usr/lib64/gconv/libGB.so
bash进程将/usr/lib64/gconv/libGB.so文件映射到以7f4f47b72000开头的地址段。
使用ldd命令,我们可以看到进程依赖的库文件,这些库文件编译过程中都要关联到进程的虚拟地址空间:
- linux # ldd /bin/bash
- linux-vdso.so.1 => (0x00007fffc73ff000)
- libreadline.so.5 => /lib64/libreadline.so.5 (0x00007f7effe50000)
- libdl.so.2 => /lib64/libdl.so.2 (0x00007f7effc4c000)
- libc.so.6 => /lib64/libc.so.6 (0x00007f7eff8ee000)
- libncurses.so.5 => /lib64/libncurses.so.5 (0x00007f7eff6a6000)
- /lib64/ld-linux-x86-64.so.2 (0x00007f7f00092000)
stack
存放局部变量,从高地址向低地址生长。
内核代码中,mm_struct结构用于描述一个进程的地址空间。
vm_area_struct用于描述一段地址,linux提供了/proc/PID/maps接口,我们可以通过该接口查询某进程的虚拟地址段:
- linux # cat /proc/$$/maps
- 00400000 - 00486000 r-xp 00000000 08:02 18104 /bin/bash
- 00586000 - 00586000 rw-p 00086000 08:02 18104 /bin/bash
- 0058b000 - 0073b000 rw-p 0058b000 00:00 0 [heap]
- 2b7c1ff67000 - 2b7c1ff82000 r-xp 00000000 08:02 12969 /lib64/ld-2.4.so
- ……
- 7fff8ab2d000 - 7fff8ab43000 rw-p 7fff8ab2d000 00:00 0 [stack]
- ffffffffff600000 - ffffffffffe00000 ---p 00000000 00:00 0 [vdso]
c库中的malloc底层通过mmap或brk实现内存申请。mmap可用于关联文件到进程的虚拟地址空间,也可用于申请匿名地址空间。
- void *mmap( void *addr, size_t length, int prot, int flags, int fd, off_t offset);
若指明fd就是关联文件到虚拟地址空间,没有指明就是申请匿名地址空间。
brk调用只是增加heap这一段虚拟内存空间,sbrk用于实际的内存申请。
Reference: Chapter 15 - The Process Address Space, Linux kernel development.3rd.Edition