bro*_*s94 10 linux memory 64-bit heap-memory virtual-memory
给定进程中堆的边界是什么?我理解这个问题可能没有简单的答案,所以我特别感兴趣的是以下答案:
我假设您正在尝试在此处编写自己的堆分配器,并且从代码中假设您在Linux中执行此操作.
SunEric给了你什么样的内存的有用指示,你可能能够使用,但是,你的内存可以使用的操作系统给你的内存.IE为了让你的进程获得内存,你需要调用操作系统将虚拟内存映射到进程空间(以及它背后的一些物理内存).malloc()为你抽象,并在C中实现'堆'.它可以通过两种方式获取它的内存:
使用brk系统调用(映射到C库brk或sbrk)
使用mmap与MAP_ANON(或更精确的底层系统调用mmap2).
brk是为堆分配内存的经典方法,通常当我们谈论'堆'时,我们指的是以这种方式brk分配的内存(虽然可以用来分配除堆之外的内存,并且堆项可能存在于其他地方 - 请参阅下面).这是brk分配工作的一个很好的答案,我无法改进.内存使用的位置实际上是算术的结果.堆在加载时遵循程序的BSS - 即BSS的值随着堆扩展而增长,因此启动实际上由OS和动态加载程序决定.因此,堆的末端由堆和堆的大小决定(即,你已经将它增长到多大).
mmap不太清楚.它需要一个addr参数:
如果
addr是NULL,则内核选择创建映射的地址; 这是创建新映射的最便携方法.如果addr不是NULL,那么内核将其作为关于放置映射的位置的提示; 在Linux上,映射将在附近的页面边界创建.作为调用的结果返回新映射的地址.
因此,如果您使用mmap为特定堆项获取空间(malloc特别是对于大型对象),则操作系统会选择其位置,无论是否有提示.如果你使用MAP_FIXED它会给你准确的位置或失败.从这个意义上讲,您的堆(或其中的项)可以是操作系统允许您映射内存的任何位置.
您询问是否有可移植的方法来查找堆的开始和结束位置.Portable意味着一种语言,我假设C.关于brk类型堆,是的(有合理的便携性).man end得到:
名称
etext,edata,end-节目段的端概要
extern etext;
extern edata;
extern end;描述
这些符号的地址表示各种程序段的结束:
etext:这是文本段末尾的第一个地址(程序代码).
edata:这是初始化数据段结束后的第一个地址.
end:这是未初始化数据段(也称为BSS段)结束后的第一个地址.
当堆从BSS加载时的末尾运行到BSS运行时的顶部时,一种方法是将endload 的值作为堆的底部作为开始,并将end作为结束时的值作为结束时的值的堆.这会错过这样一个事实:libc它本身和共享库可能会在main()调用之前分配内容.因此,一个更保守的方法是说,这是之间的区域edata和end,虽然这可能严格地说包括东西不在堆上.
如果您不是在C中,则需要使用类似的技术.取"程序中断"(即内存空间的顶部)并减去为堆提供的最低地址.
如果要查看任意进程的堆的内存分配:
$ cat /proc/$$/maps | fgrep heap
01fe6000-02894000 rw-p 00000000 00:00 0 [heap]
Run Code Online (Sandbox Code Playgroud)
替换$$您要检查的进程的PID.
在现代 64 位 AMD64 CPU 上,并非所有地址线都可以为我们提供2^64 = 16 exabytes虚拟地址空间。也许在 AMD64 架构上48启用了较低位,分别导致了2^48 = 256TB地址空间。因此理论上架构限制几乎为256TB. 因此,如果您有一个256TB允许进行交换分区的磁盘空间,您就可以获得256TB堆。如果您对交换分区的数量和大小有限制,那么256TB即使可用磁盘空间很大,您受到的限制也会更少。
在当前 AMD 的 48 位实现中,AMD64 CPU 能够以规范格式寻址的完整虚拟内存范围(如下图所示)分为从0到00007FFFFFFFFFFF和从FFFF800000000000到 的两半FFFFFFFFFFFFFFFF,导致可用虚拟地址空间总计为256TB。上半部分内存区域地址空间用于内核空间,下半部分是用于代码、堆、堆栈段的用户空间。因此,随着更多虚拟地址位的可用性,下半部分地址位向上增长,从而导致更多虚拟空间用于将不同段映射到内存中。这意味着堆可以增长到256TB最大值。
0xFFFFFFFFFFFFFFFF +-----------+
| Kernel |
| |
0xFFFF800000000000 +-----------+
| Non |
| Canonical |
| range |
0x00007FFFFFFFFFFF +-----------+
| User |
| |
0x0 +-----------+
Run Code Online (Sandbox Code Playgroud)
然而,堆从文本段上方开始增长,并且可以使用sbrk参数为 0 找到堆的一端。由于堆在调用 malloc() 时是不连续的,因此它从虚拟地址空间中的任何位置返回地址。
您不应该太担心它是如何从根源上深入工作的,因为它是在现代处理器中抽象的。