给定进程中堆的边界是什么?我知道这个问题可能没有简单的答案,所以我特别感兴趣的是以下答案:
答案 0 :(得分:5)
我假设您正在尝试在此处编写自己的堆分配器,并且从代码中假设您在Linux中执行此操作。
SunEric为您提供了可能可以使用的内存的有用指示,但是,可以使用的内存是操作系统为您提供的内存。 IE为了让你的进程获得内存,你需要调用操作系统将虚拟内存映射到进程空间(以及它背后的一些物理内存)。 malloc()
为您抽象,并实现堆#39;在C.它可以通过两种方式获得记忆:
使用brk
系统调用(映射到C库brk
或sbrk
)
将mmap
与MAP_ANON
(或更确切地说是基础系统调用mmap2
)一起使用。
brk
是为堆分配内存的经典方法,通常在我们讨论堆时,我们指的是以这种方式分配的内存(尽管brk
可以是用于分配堆以外的内存,堆项可能存在于其他地方 - 见下文)。 Here是brk
分配如何运作的绝佳答案,我无法改进。内存使用的位置实际上是算术的结果。当加载时,堆遵循程序的BSS - 即,当堆扩展时BSS的值增长,因此启动实际上由OS和动态加载器确定。因此,堆的末尾由堆和堆的大小决定(即你将它增长到多大)。
mmap
不太明确。它需要addr
参数:
如果
addr
是NULL
,那么内核会选择创建映射的地址;这是创建新映射的最便携方法。如果addr
不是NULL
,那么内核会将其作为关于放置映射的位置的提示;在Linux上,映射将在附近的页面边界创建。作为调用的结果返回新映射的地址。
因此,如果您使用mmap
来获取特定堆项的空间(因为malloc
可能特别针对大型对象),操作系统会选择其位置,无论是否有提示。如果您使用MAP_FIXED
,它会准确地为您提供该位置或失败。从这个意义上说,你的堆(或其中的项)可以是操作系统允许你映射内存的任何地方。
您询问是否有可移植的方法来查找堆的开始和结束位置。 Portable意味着一种语言,我假设C.关于brk
类型堆,是的(有合理的便携性)。 man end
给出:
NAME
etext
,edata
,end
- 程序段结束概要
extern etext;
extern edata;
extern end;
说明
这些符号的地址表示各种程序段的结束:
etext
:这是文本段末尾的第一个地址(程序代码)。
edata
:这是初始化数据段结束后的第一个地址。
end
:这是未初始化数据段(也称为BSS段)结束后的第一个地址。
由于堆在加载时从BSS
的末尾运行到运行时BSS
的顶部,因此一种方法是在加载时获取end
的值作为堆的底部开始时,作为堆的末尾进行评估时end
的值。这将错过libc
本身和共享库可能在调用main()
之前分配内容的事实。所以更保守的方法是说它是edata
和end
之间的区域,尽管这可能严格地说包括不在堆上的东西。
如果你在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能够以规范格式(如下图所示)处理的完整虚拟内存范围分为两部分,范围从0
到00007FFFFFFFFFFF
和{ {1}}到FFFF800000000000
,导致可用的虚拟地址空间总计为FFFFFFFFFFFFFFFF
。上半部分内存区域地址空间用于内核空间,下半部分是用于代码,堆,堆栈段的用户空间。因此,下半地址位向上增长,具有更多虚拟地址位的可用性,导致更多虚拟空间用于将不同段映射到存储器中。哪个意味着堆可以长到最大256TB
。
256TB
然而,堆开始在文本段长大之上,并且可以使用参数为0的 0xFFFFFFFFFFFFFFFF +-----------+
| Kernel |
| |
0xFFFF800000000000 +-----------+
| Non |
| Canonical |
| range |
0x00007FFFFFFFFFFF +-----------+
| User |
| |
0x0 +-----------+
找到它的一端。当你调用malloc()时,堆是不连续的,它从任何地方返回地址虚拟地址空间。
你不应该担心它是如何从根本深入工作的,因为它在现代处理器中是抽象的。