多个堆栈和堆放在虚拟内存中哪里?
我正在编写一个内核,需要(并且希望)将多个堆栈和堆放到虚拟内存中,但我无法弄清楚如何有效地放置它们。 普通程序如何做到这一点?
如何(或哪里)将堆栈和堆放入由32位系统提供的有限虚拟内存中,以便它们尽可能多地增长空间?
例如,当一个简单的程序加载到内存中时,其地址空间的布局可能如下所示:
[ Code Data BSS Heap-> ... <-Stack ]
在这种情况下,堆可以像虚拟内存一样大(例如直到堆栈),我相信这是堆在大多数程序中的工作原理。 没有预定义的上限。
许多程序都将共享库放在虚拟地址空间的某处。 然后有多线程程序有多个堆栈,每个线程一个。 而.NET程序有很多堆,所有这些堆必须能够以这种或那种方式发展。
我不明白如何在没有对所有堆和堆栈的大小进行预定义限制的情况下实现这种效率。
我假设你已经完成了内核的基础知识,一个可以将虚拟内存页面映射到RAM的页面错误陷阱处理程序。 下一级别,您需要一个虚拟内存地址空间管理器,用户模式代码可以从中请求地址空间。 选择可防止过度分段的分段粒度,64KB(16页)是一个很好的数字。 允许用户模式代码保留空间并提交空间。 一个4GB / 64KB = 64K x 2位的简单位图可以跟踪段状态,从而完成工作。 页面错误陷阱处理程序还需要查阅此位图以了解页面请求是否有效。
堆栈是一个固定大小的虚拟机分配,通常为1兆字节。 一个线程通常只需要一小部分页面,具体取决于函数的嵌套级别,因此请保留1MB并只提交前几页。 当线程嵌套更深时,它会跳出页面错误,并且内核可以简单地将额外页面映射到RAM以允许线程继续。 您需要将底部的几页标记为特殊页面,当页面出现故障时,您声明本网站的名称。
堆管理器最重要的工作是防止碎片。 最好的办法是创建一个按大小对堆请求进行分区的后备列表。 小于8个字节的内容来自第一个分段列表。 第二个为8至16,第三个为16至32,等等。 随着时间的推移,增加大小桶。 您将不得不使用桶尺寸来获得最佳平衡。 非常大的分配直接来自VM地址管理器。
第一次在旁视列表中输入一个条目时,您将分配一个新的VM段。 您可以使用链表将细分细分为更小的块。 当这样的分配被释放时,您将该块添加到空闲块列表中。 无论程序请求如何,所有块都具有相同的大小,因此不会有任何碎片。 当段被完全使用并且没有空闲块可用时,您分配一个新的段。 当一个段只包含空闲块时,您可以将其返回给VM管理器。
该方案允许您创建任意数量的堆栈和堆。
简而言之,由于您的系统资源总是有限的,因此您无法走向无限。
内存管理总是由几个层组成,每个层都有明确的责任。 从程序的角度来看,应用程序级管理器是可见的,通常只涉及它自己的单个分配的堆。 上面的级别可以处理如果需要从一个全局堆中创建多个堆并将它们分配给子程序(每个都有自己的内存管理器)。 上面可能是它使用的标准malloc()
/ free()
,并且高于那些处理页面的操作系统和每个进程的实际内存分配(它基本上不关心多堆,而且甚至是用户级堆一般)。
内存管理成本很高,因此陷入内核。 将两者结合起来可能会造成严重的性能下降,所以从应用程序的角度来看,实际的堆管理实际上是为了性能而在用户空间(C运行时库)中实现的(以及其他原因超出了现在的范围)。
当加载共享(DLL)库时,如果它在程序启动时加载,它当然很可能会加载到CODE / DATA /等,因此不会发生堆碎片。 另一方面,如果它是在运行时加载的,那么除了占用堆空间几乎没有其他可能。 静态库当然只是简单地链接到CODE / DATA / BSS / etc部分。
在一天结束时,你需要对堆和堆栈施加限制,以防止它们溢出,但你可以分配其他的。 如果需要超出这个限制,你也可以
free()
通常表现不佳的原因。 考虑到平均每个call
都有1KB的堆栈帧(如果应用程序开发人员没有经验的话,可能会发生这种情况),对于10240嵌套call
10MB堆栈就足够了。 顺便说一句,除此之外,几乎不需要每个线程有多个堆栈和堆。
上一篇: Where are multiple stacks and heaps put in virtual memory?
下一篇: s unlimited" do?