堆的界限是什么?

给定过程中堆的界限是什么? 我明白,这个问题可能没有简单的答案,所以我特别感兴趣的答案如下:

  • 在AMD64的Linux下,是否有标准堆大小/位置的64位进程
  • 如果我正在实现一个语言运行时,我怎么能find我不允许堆的地方(再次,Linux / AMD64)
  • 有没有一种便捷的方式来查找应用程序的开始/结束位置?

我假设你正在试图在这里写你自己的堆分配器,并从标签假设你正在做的Linux。

SunEric给了你一个有用的信息,告诉你可以使用什么内存,但是你可以使用的内存是操作系统给你的内存。 IE为了让你的进程获得内存,你需要调用操作系统把虚拟内存映射到进程空间(以及它后面的一些物理内存)。 malloc()为你抽象,并在C中实现“堆”。它可以通过两种方式获得它的内存:

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

  2. 使用mmapMAP_ANON (或更准确地说是底层系统调用mmap2 )。

brk是为堆分配内存的经典方法,通常当我们谈论“堆”的时候,我们指的是以这种方式分配的内存(尽管brk可以用来分配除堆以外的内存,堆项可能存在于别处- 见下文)。 这是一个很好的答案,如何brk分配的作品,我无法改善。 内存使用的位置实际上是算术的结果。 当堆被加载时,堆遵循程序的BSS – 即BSS的值随着堆扩展而增长,所以启动确实由OS和动态加载器决定。 堆的末尾是由堆和堆的大小决定的(也就是说,堆的大小是多少)。

mmap不太清楚。 它需要一个addr参数:

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

所以,如果你使用mmap来获取特定堆项的空间( malloc可能特别适用于大型对象),操作系统会选择它的位置,无论有没有提示。 如果你使用MAP_FIXED它会给你完全的位置或失败。 从这个意义上说,你的堆(或其中的项目)可以在任何地方操作系统将让你映射内存。

你问是否有一种便携的方式来找出堆开始和结束的地方。 便携式意味着一种语言,我会假设C.关于brk类型的堆,是的(合理便携)。 man end给:

名称

etextedata ,程序段的结尾

概要

extern etext;

extern edata;

extern end;

描述

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

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

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

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

由于堆在运行时从运行时的BSS末端运行到运行时的BSS顶部,因此一种方法是以load end的值作为堆的底部,并在end时评估为堆的结束。 这会错过libc本身和共享库可能在main()被调用之前分配的事实。 所以比较保守的做法就是说它是edataend之间的区域,虽然这可能严格地说是包括了不在堆上的东西。

如果你的意思不是C,你需要使用类似的技术。 采取“程序中断”(即内存空间的顶部),并减去你给你的堆的最低地址。

如果您想查看任意进程的堆的内存分配情况,请执行以下操作:

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

用您想要检查的进程的PID替换$$

在现代的64位AMD64 CPU上,不是所有的地址线都能够为我们提供2^64 = 16 exabytes的虚拟地址空间。 也许在AMD64架构上有48低位使能,分别导致2^48 = 256TB的地址空间。 因此,理论上的架构限制接近256TB 。 所以如果你有一个256TB256TB的交换分区磁盘空间,你可以得到256TB的堆。 如果您对交换分区的数量和大小有任何限制,即使可用磁盘空间很大,您仍然受限于小于256TB

在目前AMD的48位实现中,AMD64 CPU能够以规范格式(如下图所示)能够寻址的完整虚拟内存范围是从000007FFFFFFFFFFF和从FFFF800000000000FFFFFFFFFFFFFFFF两半,导致可用的虚拟地址空间总计到256TB 。 内核空间的上半部分地址空间,下半部分是代码,堆,堆栈段的用户空间。 因此,低半地址位随着更多虚拟地址位的可用性而向上生长,从而引导更多的虚拟空间以将不同的段映射到存储器中。 这意味着堆可以长到256TB最大值。

  0xFFFFFFFFFFFFFFFF +-----------+ | coreel | | | 0xFFFF800000000000 +-----------+ | Non | | Canonical | | range | 0x00007FFFFFFFFFFF +-----------+ | User | | | 0x0 +-----------+ 

然而,堆在文本段成长的上方开始,并且可以使用带参数的sbrk来找到它的一端。当你调用malloc()时,堆是不连续的,它会从虚拟地址空间的任何地方返回地址。

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