关于Linux程序内存布局模式的问题

关于Linux程序内存布局模式的问题,linux,memory-management,linux-kernel,Linux,Memory Management,Linux Kernel,下图引自该书 为什么程序内存区域限制在0xbffffff和0x8048000之间?这一选择背后的理由是什么?这个地区以外有什么 这张图片显示的应该是一个32位程序。64位程序的内存布局是什么 图中提到“启动时”,那么在运行期间布局会发生变化吗 最后,Linux内核也遵循这种布局吗 32位x86上的Linux传统上有3G/1G用户/内核划分 用户空间内存始终映射在0x00000000-0xBFFFFFFF范围内(即4GB中可以在32位模式下直接寻址的较低3GB)。在进程之间切换时,将重新映

下图引自该书

  • 为什么程序内存区域限制在
    0xbffffff
    0x8048000
    之间?这一选择背后的理由是什么?这个地区以外有什么

  • 这张图片显示的应该是一个32位程序。64位程序的内存布局是什么

  • 图中提到“启动时”,那么在运行期间布局会发生变化吗

  • 最后,Linux内核也遵循这种布局吗

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

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

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

        我有一个更详细的计划

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

        相反,默认情况下,程序数据映射到低端。从历史上看,内核映射在
        0x08000000
        下方,因此可供用户空间使用的最低地址位于该地址上方。这不再正确,但它解释了原始的
        0x0804000
        加载地址

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

      • 在当前64位x86 CPU上,只有地址0x0000000000000000-0x00007FFFFFFFFFFFFFFF和0xFFFF80000000000-0xFFFFFFFFFFFFFF是规范的。64位指针,但“只有”48位的可用地址空间。(这是256TB,所以我们需要一段时间才能达到该限制。)如果您尝试访问任何非规范地址(0x000008000000000-0xFFFF7FFFFFFFFFFF),硬件将触发故障。Linux在下半部分映射用户空间,在上半部分映射内核

        堆栈仍在向下增长,因此Linux仍会朝着范围的顶部启动,而固定映射则朝着底部启动,堆在其上方增长

      • 对。除了程序分解会随着堆的增长和收缩而上下移动外,程序还可以使用
        mmap
        /
        munmap
        将各种内容映射/取消映射到其地址空间,例如文件、共享内存、匿名内存等。事实上,现在的C运行时除了操纵程序中断之外,还将匿名内存映射成块

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

      其他说明:

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

      用户空间可访问的最低地址不一定是0。C程序期望
      NULL
      是一个错误的地址(这可能会导致它是否实际有效),内核现在强制执行默认为64k的地址

      现在的Linux(现在已经超过十年了)将数据映射到一个极高的地址,因此堆栈从这个地址开始。从前,vsyscall页也在上面,但现在它位于内核空间的一页中

      由于错误,所有地址都可能被乱排。这在32位地址空间上不是很有效(由于页面对齐,不能将较低的12位随机化,可能由于其他限制而使其随机化),但在64位模式下有很多位。

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

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

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

        我有一个更详细的计划

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

        相反,默认情况下,程序数据映射到低端。从历史上看,内核映射在
        0x08000000
        下方,因此可供用户空间使用的最低地址位于该地址上方。这不再正确,但它解释了原始的
        0x0804000
        加载地址

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

      • 在当前64位x86 CPU上,