堆的边界是什么?

时间:2014-02-18 16:35:27

标签: linux memory 64-bit heap-memory virtual-memory

给定进程中堆的边界是什么?我知道这个问题可能没有简单的答案,所以我特别感兴趣的是以下答案:

  • AMD64上Linux下的64位进程是否有标准堆大小/位置?
  • 如果我正在实现语言运行时,我怎样才能找到我不允许放入堆的地方(同样,Linux / AMD64)
  • 应用程序是否有可移植的方式来查找它的开始/结束位置?

2 个答案:

答案 0 :(得分:5)

我假设您正在尝试在此处编写自己的堆分配器,并且从代码中假设您在Linux中执行此操作。

SunEric为您提供了可能可以使用的内存的有用指示,但是,可以使用的内存是操作系统为您提供的内存。 IE为了让你的进程获得内存,你需要调用操作系统将虚拟内存映射到进程空间(以及它背后的一些物理内存)。 malloc()为您抽象,并实现堆#39;在C.它可以通过两种方式获得记忆:

  1. 使用brk系统调用(映射到C库brksbrk

  2. mmapMAP_ANON(或更确切地说是基础系统调用mmap2)一起使用。

  3. brk是为堆分配内存的经典方法,通常在我们讨论堆时,我们指的是以这种方式分配的内存(尽管brk可以是用于分配堆以外的内存,堆项可能存在于其他地方 - 见下文)。 Herebrk分配如何运作的绝佳答案,我无法改进。内存使用的位置实际上是算术的结果。当加载时,堆遵循程序的BSS - 即,当堆扩展时BSS的值增长,因此启动实际上由OS和动态加载器确定。因此,堆的末尾由堆和堆的大小决定(即你将它增长到多大)。

    mmap不太明确。它需要addr参数:

      

    如果addrNULL,那么内核会选择创建映射的地址;这是创建新映射的最便携方法。如果addr不是NULL,那么内核会将其作为关于放置映射的位置的提示;在Linux上,映射将在附近的页面边界创建。作为调用的结果返回新映射的地址。

    因此,如果您使用mmap来获取特定堆项的空间(因为malloc可能特别针对大型对象),操作系统会选择其位置,无论是否有提示。如果您使用MAP_FIXED,它会准确地为您提供该位置或失败。从这个意义上说,你的堆(或其中的项)可以是操作系统允许你映射内存的任何地方。

    您询问是否有可移植的方法来查找堆的开始和结束位置。 Portable意味着一种语言,我假设C.关于brk类型堆,是的(有合理的便携性)。 man end给出:

      

    NAME

         

    etextedataend - 程序段结束

         

    概要

         

    extern etext;

         

    extern edata;

         

    extern end;

         

    说明

         

    这些符号的地址表示各种程序段的结束:

         
        
    • etext:这是文本段末尾的第一个地址(程序代码)。

    •   
    • edata:这是初始化数据段结束后的第一个地址。

    •   
    • end:这是未初始化数据段(也称为BSS段)结束后的第一个地址。

    •   

    由于堆在加载时从BSS的末尾运行到运行时BSS的顶部,因此一种方法是在加载时获取end的值作为堆的底部开始时,作为堆的末尾进行评估时end的值。这将错过libc本身和共享库可能在调用main()之前分配内容的事实。所以更保守的方法是说它是edataend之间的区域,尽管这可能严格地说包括不在堆上的东西。

    如果你在C中没有意思,你需要使用类似的技巧。采取“节目休息”#39; (即内存空间的顶部)并减去为堆提供的最低地址。

    如果要查看任意进程的堆内存分配:

    $ cat /proc/$$/maps | fgrep heap
    01fe6000-02894000 rw-p 00000000 00:00 0                                  [heap]
    

    $$替换为您要检查的进程的PID。

答案 1 :(得分:3)

在现代64位AMD64 CPU上,并非所有地址线都能够为我们提供虚拟地址空间2^64 = 16 exabytes。也许在AMD64架构上,分别启用了48个低位,从而导致2^48 = 256TB地址空间。因此理论上,架构限制几乎为256TB。因此,如果您拥有允许进行交换分区的256TB磁盘空间,则可以获得256TB堆。如果你有数字和限制的限制即使可用磁盘空间很大,交换分区的大小也会限制在256TB以下。

在当前AMD的48位实现中,AMD64 CPU能够以规范格式(如下图所示)处理的完整虚拟内存范围分为两部分,范围从000007FFFFFFFFFFF和{ {1}}到FFFF800000000000,导致可用的虚拟地址空间总计为FFFFFFFFFFFFFFFF。上半部分内存区域地址空间用于内核空间,下半部分是用于代码,堆,堆栈段的用户空间。因此,下半地址位向上增长,具有更多虚拟地址位的可用性,导致更多虚拟空间用于将不同段映射到存储器中。哪个意味着堆可以长到最大256TB

256TB

然而,堆开始在文本段长大之上,并且可以使用参数为0的 0xFFFFFFFFFFFFFFFF +-----------+ | Kernel | | | 0xFFFF800000000000 +-----------+ | Non | | Canonical | | range | 0x00007FFFFFFFFFFF +-----------+ | User | | | 0x0 +-----------+ 找到它的一端。当你调用malloc()时,堆是不连续的,它从任何地方返回地址虚拟地址空间。

你不应该担心它是如何从根本深入工作的,因为它在现代处理器中是抽象的。