2013-04-22 133 views
5

我正在编写一个内核,需要(并且想要)将多个堆栈和堆放到虚拟内存中,但我无法弄清楚如何有效地放置它们。普通程序如何做到这一点?多个堆栈和堆放在虚拟内存中的位置?

如何(或哪里)将堆栈和堆放入由32位系统提供的有限虚拟内存中,以便它们具有尽可能多的增长空间?

例如,当一个简单的程序被加载到内存中,其地址空间的布局可能是这样的:

[ Code Data BSS Heap-> ... <-Stack ] 

在这种情况下,堆可以成长为大的虚拟内存允许(例如最多到堆栈),我相信这是这个堆在大多数程序中的工作原理。没有预定义的上限。

许多程序都具有放置在虚拟地址空间某处的共享库。 然后有多线程程序有多个堆栈,每个线程一个。而.NET程序有multiple heaps,所有这些都必须能够以这种或那种方式发展。

我只是没有看到如何在没有对所有堆和堆栈的大小进行预定义限制的情况下合理高效地完成此操作。

回答

0

简而言之,由于您的系统资源总是有限的,所以您不能无限制地使用。

内存管理总是由多个层组成,每个层都有明确的责任。从程序的角度来看,应用程序级管理器是可见的,通常只涉及它自己的单个分配的堆。上面的级别可以处理如果需要从一个全局堆中创建多个堆并将它们分配给子程序(每个都有自己的内存管理器)。以上可能是它使用的标准malloc()/free(),并且高于那些处理页面和每个进程的实际内存分配的操作系统(它基本上不关心多个堆,而是一般不关心用户级堆)。

内存管理代价高昂,陷入内核也是如此。将两者结合起来可能会造成严重的性能下降,所以从应用程序的角度来看,实际的堆管理实际上是为了性能而在用户空间(C运行时库)中实现的(以及其他原因超出了现在的范围)。

当加载一个共享(DLL)库时,如果它在程序启动时加载,它当然最有可能加载到CODE/DATA /等,因此不会发生堆碎片。另一方面,如果它是在运行时加载的,那么除了占用堆空间几乎没有其他可能。 静态库当然只是简单地链接到CODE/DATA/BSS/etc部分。

在一天结束时,您需要对堆栈和堆栈施加限制,以避免溢出,但您可以分配其他堆栈。 如果一个人需要成长超出上限,您可以

  • 终止与错误的应用程序
  • 有内存管理器分配/调整/移动存储块为堆栈/堆和最有可能整理堆(自己的水平);这就是为什么free()通常表现不佳。

考虑每个call为平均一个相当大的,1KB堆栈帧(如果应用程序开发人员雏可能发生)一个10MB的堆栈就足够了10240嵌套call -s。顺便说一句,除此之外,几乎不需要每个线程有多个堆栈和堆。

+0

但是在线程之间切换不应该切换整个地址空间(并且导致TLB被刷新),因此对于进程使用的每个线程的每个线程,其堆栈_必须出现在进程的地址空间中。我的文章中的链接显示了一个CLR过程如何有很多堆的图像。所以这里需要一个以上的堆栈并堆放在一个地址空间中。 – Virtlink 2013-04-25 14:46:10

+0

地址空间是一个非常不同的抽象层次。实际上,在这种情况下,您在同一个地址空间中有多个堆栈和堆栈。地址空间本身由OS管理,而堆不是;它由用户级库代码管理。 – Powerslave 2013-04-25 15:37:04

2

我假设你已经完成了内核的基础知识,可以将虚拟内存页面映射到RAM的页面错误陷阱处理程序。下一级别,您需要一个虚拟内存地址空间管理器,用户模式代码可以从中请求地址空间。选择可防止过度分段的分段粒度,64KB(16页)是一个很好的数字。允许用户模式代码保留空间并提交空间。一个4GB/64KB = 64K x 2位的简单位图可以跟踪段状态,从而完成工作。页面错误陷阱处理程序还需要查阅此位图以了解页面请求是否有效。

堆栈是一个固定大小的VM分配,通常为1兆字节。一个线程通常只需要一小部分页面,具体取决于函数的嵌套级别,因此请保留1MB并只提交前几页。当线程嵌套更深时,它会跳出页面错误,并且内核可以简单地将额外页面映射到RAM以允许线程继续。您需要将底部的几页标记为特殊页面,当页面出现故障时,您声明本网站的名称。

堆管理器最重要的工作是防止碎片。最好的办法是创建一个按大小对堆请求进行分区的后备列表。小于8个字节的内容来自第一个分段列表。第二个为8至16,第三个为16至32,等等。随着时间的推移,增加大小桶。您将不得不使用桶尺寸来获得最佳平衡。非常大的分配直接来自VM地址管理器。

第一次点击旁视列表中的条目时,您将分配一个新的VM段。您可以使用链表将细分细分为更小的块。当这样的分配被释放时,您将该块添加到空闲块列表中。无论程序请求如何,所有块都具有相同的大小,因此不会有任何碎片。当段被完全使用并且没有空闲块可用时,您分配一个新的段。当一个段只包含空闲块时,您可以将其返回给VM管理器。

该方案允许您创建任意数量的堆栈和堆。

+0

你很好地描述了堆是如何工作的,但我不明白这个“方案”如何让我创建任何数量的堆。如果我在用户的代码和数据后立即为第一个堆保留16 MB,那么我该在哪里放第二个堆?第一个之后?那么第一个堆不能超过其最初的16 MB。或堆栈扩展(分割堆)的片段,但这对于缓存局部性(例如高频堆),最大对象大小(例如大对象堆),垃圾收集,或者出于任何原因使用多个堆都是不利的。例如。 .NET有很多堆,他们怎么做? – Virtlink 2013-04-30 08:57:52

+0

您错过了堆分配是分段的部分,而不是固定的大小。一个堆在增长时会有很多段,它们可以散布在整个地址空间中。缓存局部性通常非常好,因为数组具有来自相同后备列表链的固定大小的元素。 – 2013-04-30 09:39:08