我对Window堆分配和堆堆栈等很清楚。尽管不是Linux的新手,但我不太清楚它是如何工作的?
在Windows上:
b)作为初始化的一部分,CRT为其所有分配创建一个额外的堆(此CRT堆的句柄内部存储在CRT库中的全局变量_crtheap中)。
c)CRT创建自己的私有(private)堆,该堆位于Windows堆的顶部。
d)Windows堆是围绕Windows运行时分配器(NTDLL)的薄层。
e)Windows运行时分配器与虚拟内存分配器进行交互,虚拟内存分配器保留并提交操作系统使用的页面。
我们的DLL和exe链接到多线程静态CRT库。我们创建的每个DLL和exe文件都有一个自己的堆,即_crtheap。分配和取消分配必须从各自的堆中进行。从DLL动态分配的,不能从可执行文件中取消分配,反之亦然。
使用/ MD或/ MDd在DLL和exe中编译我们的代码,以使用运行库的特定于多线程和DLL的版本,会将DLL和exe链接到相同的C运行时库,并因此链接一个_crtheap。分配始终与单个模块中的取消分配配对。
这与Liunx上的行为相同吗?
那里有什么堆?那么CRT堆呢?
最佳答案
我真的不知道该如何回答,但是我将尝试提供足够的常规信息,以帮助您了解需要知道的内容。
作为unix的人,我从您的问题中学到的Windows内存管理内容是,你们经常使用“堆”这个词。除了作为“由malloc管理的内存区域”的非正式同义词外,我们没有。
有2个主要的动态内存分配原语需要注意:brk
和mmap
。所有其他分配函数(包括malloc
)都建立在这些函数之上。brk
是旧的。它的工作原理是,在bss段结束之后,只需将更多内存添加到进程的虚拟内存映射中即可。您向brk
传递一个值,该值成为进程的“中断”地址-分配的虚拟内存的末尾。
可以在brk
之上构建malloc,方法是在每次需要更多内存时以更高的新值对其进行调用,并维护一些内部数据结构来跟踪已释放的内容和仍在使用的内容。 (经典实现中没有将内存免费释放回系统。)某些malloc实现的内部数据结构必须是heap,给brk段起了绰号:“堆”。
另请参阅:What does brk( ) system call do?(有图片!)
直接调用brk
的程序真的很不寻常。 (甚至是薄包装sbrk
)。普通程序中对brk
的每次使用都是通过malloc
进行的。请记住,尽管您将C库(包括malloc
)视为某种可选的额外功能,但我们的操作系统与C紧密结合,因此对于内存管理等低级内容,不使用libc的程序非常有用。确实很奇怪。因此,大多数时候,brk段(也就是“堆”)中的所有内存都是由malloc管理的。但是,事实并非如此,因为...mmap
,比brk
更新(mmap来自90年代; brk来自70年代)提供了很多选择。当您想将文件映射到内存中,或者分配几个不连续的内存块,而不仅仅是在原始数据块的末尾添加一些空间时,可以使用mmap
。共享库加载器使用mmap映射每个库的文本和数据。现代的malloc实现将mmap用于大型请求,将brk用于小型请求。我们还有mremap
,它将映射重新定位到新的虚拟地址,同时将其保持在相同的物理地址,从而允许重新分配以避免昂贵的副本。
如果在Linux上查看/proc/$PID/maps
,则会看到一个标记为[heap]
的内存区域。那是brk细分市场。每个过程中只有一个。 (我看过一些示例,其中maps
文件显示了其中两个,但是它们是连续的并且具有相同的属性,因此实际上等效于单个区域。我不知道是什么原因导致重复列出。)
考虑到所有这些背景,分配“额外堆”将意味着什么?您可以使用mmap从系统请求一些内存,从而为您提供一个独立于malloc的区域。然后,您可以对该区域进行类似malloc的管理,将其分批分发给各种大小的块,并跟踪未使用的部分。但是您的新分配器将不会成为“堆”。这实际上没有任何意义,因为系统对堆一无所知。