有关Linux程序内存布局架构的问题

时间:2017-02-03 02:37:47

标签: linux memory-management linux-kernel

下面的图片引用了Programming from the Ground Up一书。

enter image description here

  • 为什么程序存储区域限制在0xbfffffff0x8048000之间?这个选择背后的理由是什么?这个地区以外的地方是什么?

  • 此图片说明的应该是32位程序。 64位程序的内存布局是什么?

  • 图片提到"在启动时#34;在运行期间布局是否也会改变?

  • 最后但并非最不重要的是,Linux内核是否也遵循这种布局?

1 个答案:

答案 0 :(得分:4)

  • 32位x86上的Linux传统上有3G / 1G用户/内核分裂。

    • 用户空间内存始终映射在0x00000000-0xBFFFFFFF范围内(即3GB中较低的3GB,可以在32位模式下直接寻址)。在进程之间切换时,将重新映射整个空间。

    • 内核内存始终映射在0xC0000000-0xFFFFFFFF范围内(高1GB)。它在用户空间的权限级别是不可访问的,但是当上下文切换到内核时,它可以访问其所有内存而无需重新映射任何内容。

    我有一个older answer更详细地介绍了这一点。

  • 在x86上,堆栈向下增长(从高处开始向低地址移动)。这在CPU设计中几乎是一个随意的决定(push / pop / call / ret指令对%esp的作用是什么)。 Linux在该范围的顶端启动它。

    相反,程序数据默认映射到低端。从历史上看,内核映射到0x08000000以下,因此可供用户空间使用的最低地址高于该值。这已不再适用,但它解释了原始的0x08040000加载地址。

  • 中间的空格用于堆。 " Break"标记通过调用brk()sbrk()来上移/下移;它下面的内存可供程序使用。从历史上看,C运行时会将程序分解以满足malloc的空间要求。

  • 在当前的64位x86 CPU上,只有地址0x0000000000000000-0x00007FFFFFFFFFFF和0xFFFF800000000000-0xFFFFFFFFFFFFFFFF是规范的。 64位指针,但"仅#34; 48位可用的地址空间。 (这是256TB,因此它在我们达到该限制之前会有一段时间。)如果您尝试访问任何非规范地址(0x0000800000000000-0xFFFF7FFFFFFFFFFF),硬件将触发故障。 Linux在下半部分映射用户空间,在较高部分映射内核。

    堆栈仍在增长,因此Linux仍然将其启动到范围的顶部,固定映射从底部开始,堆积在它们之上。

  • 是。除了在堆增长和缩小时上下移动程序时,程序还可以使用mmap / munmap将各种内容映射/取消映射到其地址空间,例如文件,共享内存事实上,这些天的C运行时除了操纵程序中断之外或者代替操作程序中断,还是以块的形式映射匿名内存。

  • 内核本身位于32位x86的高1G和64位x86的高128TB。它的布局对用户空间来说大部分是不重要的(并且是不可见的),但包括每个内核线程的堆栈和每个用户线程的内核端,页表,缓存,DMA缓冲区等。

其他说明:

所有这些都是关于虚拟地址,而不是物理地址。

用户空间可访问的最低地址不一定是0. C程序期望NULL是一个错误的地址(如果它实际上有效,则可以导致exploits),并且内核强制执行{{ 3}}现在默认为64k。

Linux这些天(现在已经十多年了)将/proc/sys/vm/mmap_min_addr映射到一个极高的地址,因此堆栈从低于此开始。曾几何时,vsyscall页面也在那里,但现在它存在于内核空间页面中。

由于VDSO,所有地址都可能会被洗牌。这在32位地址空间上并不是非常有效(由于页面对齐,您可以随机化低12位,可能更多是由于其他约束),但是有很多位64位模式。