进程地址空间的布局(整理)
虚拟地址空间中包含了若干区域。其分布方式是特定于体系结构的,但所有方法都有下列共同成分。
1)当前运行的二进制代码。该代码通常称之为text,所处的虚拟内存区域称之为text段。
2)程序使用的动态库的代码
3)用于保存全局变量和动态产生的数据的堆(应该还包括静态变量)
4)用于保存局部变量和实现函数/过程调用的栈。
5)环境变量和命令行参数的段
6)将文件内容映射到虚拟地址空间中的内存映射。
下图说明了大多数体系结构的虚拟地址地址空间中的布局情况。
text段如何映射到虚拟地址空间中由ELF标准确定。每个体系结构都指定了一个特定的起始地址:IA-32系统起始于0x08048000,在text段的起始地址与最低的可用地址之间有大约128M的间距,用于捕获NULL指针。其他体系结构也有类似的缺口:UltraSparc计算机使用0x100000000作为text段的起始点,而AMD64使用0x0000000000400000。堆紧接着text段开始,向上增长。
栈起始于STACK_TOP,如果设置了PF_RANDOMIZE,则起始点会减少一个小的随机值。每个体系结构都必须定义STACK_TOP,大多数都设置为TASK_SIZE,即用户地址空间中最高的可用地址。进程的参数列表和环境变量都是栈的初始数据。
用于内存映射的区域起始于mm_struct->mmap_base,通常设置为TASK_UNMAPPED_BASE,每个体系结构都需要定义。几乎所有的情况下,其值都是TASK_SIZE/3。要注意,如果使用内核的默认设置,则mmap区域的起始点不是随机的。
如果计算机提供了巨大的虚拟地址空间,那么使用上述的地址空间布局会工作得非常好。但在32位计算机上可能会出现问题。考虑IA-32的情况:虚拟地址空间从0到0xC0000000,每个用户进程有3G可用。TASK_UNMAPPED_BASE起始于0x40000000,即1G处。糟糕的是,这意味着堆只有1G空间可供使用,继续增长则会进入到mmap区域,这显然不是我们想要的。
问题在于,内存映射区域位于虚拟地址空间的中间。这也是在内核版本2.6.7开发期间为IA-32计算机引入一个新的虚拟地址空间布局的原因(经典布局仍然可以使用)。新的布局如下图所示:
其想法在于使用固定值限制栈的最大长度。由于栈是有界的,因此安置内存映射的区域可以在栈末端的下方立即开始。与经典方法相反,
该区域现在是自顶向下扩展。由于堆仍然位于虚拟地址空间中较低的区域并向上增长,因此mmap区域和堆可以相对扩展,直至耗尽虚拟地址空间中剩余的区域。为确保栈与mmap区域不发生冲突,两者之间设置了一个安全隙。