概述
我们都知道一个进程跑起来后,其代码段、数据段、堆、栈等都分布在虚拟地址空间的不同位置。一个大致的内存映射图(ARM/MIPS架构均是如此)如下:
首先需要说明的是,这里说的都是虚拟地址空间,即对于32位系统来说的0~4G的空间;另外,进程中每个线程的栈、特有变量都是各自独有的,而代码段、只读数据、动态库代码段、堆都是所有线程共享的。
我们可以通过/proc下面的maps文件查看到一个进程的主线程的虚拟地址各区域的分布情况。
root@robdev:~# cat /proc/12443/maps
00008000-00009000 r-xp 00000000 1f:07 40 /root/procmap
00010000-00011000 rw-p 00000000 1f:07 40 /root/procmap
01c55000-01c56000 rw-p 00000000 00:00 0 [heap]
40018000-40019000 rw-p 00000000 00:00 0 //thread local变量
40021000-40022000 rw-p 00000000 00:00 0
40049000-40050000 r-xp 00000000 1f:06 436 /lib/ld-uClibc.so.0
40057000-40058000 rw-p 00006000 1f:06 436 /lib/ld-uClibc.so.0
4007d000-4007e000 rw-p 00000000 00:00 0
40082000-40083000 rw-p 00000000 00:00 0
4008a000-4008b000 rw-s 00000000 00:04 37168 /dev/zero (deleted) //调用mmap(shared)
401ea000-40276000 r-xp 00000000 1f:06 349 /lib/libc.so.0
40276000-4027d000 ---p 00000000 00:00 0
4027d000-4027f000 rw-p 0008b000 1f:06 349 /lib/libc.so.0
4027f000-40485000 rw-p 00000000 00:00 0
40485000-40585000 rw-s 00000000 00:04 37169 /dev/zero (deleted) //调用mmap(shared)
405f6000-406f6000 rw-p 00000000 00:00 0 //调用mmap(private)
bedbd000-bedde000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
这里我列一些常见情况(按上面smap的打印自上至下的顺序):
程序代码段和只读数据段:即上面第一行,可以看到这段区域的权限是r-x,且是private的。
可读写的程序数据段:比如可修改的(初始化或未初始化的)全局变量。
堆:即heap,动态分配的内存,如malloc较小的内存,向高地址增长。
mmap映射区:包括动态库代码和数据段(只读和可读写)、用户调用mmap映射的页、以及malloc较大块的内存,都会在这个区域。
栈:即stack,从用户态高处某个地址开始,向低地址延伸。
下面我举几个例子:
- 全局static变量:数据段,即上面第二行。
- 全局const变量:只读数据段,跟代码段在一起,因为无需修改,即上面第一行。
- 常量:例如 char * str = “I love you.”; 这里str指向的字符常量,跟代码段在一起,因为不会被修改。这样的话,如果另一个变量也赋值为 “I love you.”,则同指向这个字符常量。当然,str变量本身是局部变量,位于函数栈中。
- 局部const变量:函数栈中,因为它只是一个不能修改的局部变量。
- 局部static变量:数据段,即上面第二行。编译器会额外标记它的作用域在函数内部。
- 普通局部变量:函数栈中。
- thread local变量:在mmap映射区附近,上面我做了注释,但我不确定是否位于mmap映射区。这是每个线程独有的全局变量(如errno)。
- malloc(16):在堆上(通过chunk管理或sbrk申请的内存),即上面的[heap]。
- malloc (2 * 1024 * 1024):mmap映射区,在动态库映射区的上面,栈下面。至于什么时候会使用mmap()分配内存,可通过mallopt()修改M_MMAP_THRESHOLD来设置(默认是128*1024,即超过128kB就用mmap())。提醒一点,用mmap分配的内存在free的时候会立即归还给系统,而sbrk则不会立即归还(可借助mallopt()调整M_TRIM_THRESHOLD做一些水位控制)。
- mmap(NULL, 64, …, MAP_SHARED|MAP_ANONYMOUS, …):mmap映射区,可能因为size较小?在ld-uClibc.so和libc.so中间,上面我做了注释,注意权限中的’s’。
- mmap(NULL, 1024*1024, …, MAP_SHARED|MAP_ANONYMOUS, …):mmap映射区。
- mmap(NULL, 1024*1024, …, MAP_PRIVATE|MAP_ANONYMOUS, …):mmap映射区,注意权限中的’p’。
补充一点,上面是以虚拟内存块为单位描述一个程序各部分的大致分布。这和细致到每一section的分布情况并不冲突,例如,已初始化的全局const变量和字符串常量是位于.rodata段的,它和代码段.text挨着并且权限类似,所以被放在了同一个虚拟内存地址块。又如,已初始化和未初始化和全局(静态)变量分别位于.data和.bss段,他俩也是挨着且权限相同,因此也被放在了同一个虚拟内存地址块。
——
最后
以上就是冷静裙子为你收集整理的应用程序中各区域的内存映射位置的全部内容,希望文章能够帮你解决应用程序中各区域的内存映射位置所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复