Operating system 虚拟内存中的多个堆栈和堆放在哪里?

Operating system 虚拟内存中的多个堆栈和堆放在哪里?,operating-system,stack,heap,virtual-memory,Operating System,Stack,Heap,Virtual Memory,我正在编写一个内核,需要(并且想要)将多个堆栈和堆放入虚拟内存中,但我不知道如何有效地放置它们。普通程序是如何做到这一点的 如何(或在何处)将堆栈和堆放入32位系统提供的有限虚拟内存中,以使它们具有尽可能多的增长空间 例如,当一个普通程序加载到内存中时,其地址空间的布局可能如下所示: [ Code Data BSS Heap-> ... <-Stack ] 简而言之,由于您的系统资源总是有限的,所以您不能无限地使用 内存管理总是由几个层组成,每个层都有其明确的职责。从

我正在编写一个内核,需要(并且想要)将多个堆栈和堆放入虚拟内存中,但我不知道如何有效地放置它们。普通程序是如何做到这一点的

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

例如,当一个普通程序加载到内存中时,其地址空间的布局可能如下所示:

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

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

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

内存管理成本很高,因此会被困在内核中。将二者结合起来可能会对性能造成严重影响,因此从应用程序的角度来看,实际的堆管理实际上是在用户空间(C运行时库)中实现的,这是为了性能(以及目前不在范围内的其他原因)

加载共享(DLL)库时,如果在程序启动时加载,当然很可能会加载到代码/数据/等中,因此不会发生堆碎片。另一方面,如果在运行时加载,除了耗尽堆空间外,几乎没有其他机会。 当然,静态库只是链接到代码/数据/BSS/etc部分

在一天结束时,您需要对堆和堆栈施加限制,以便它们不太可能溢出,但您可以分配其他堆和堆栈。 如果一个人需要超越这个极限,你可以

  • 终止应用程序时出错
  • 让内存管理器为该堆栈/堆分配/调整大小/移动内存块,然后很可能对堆(其自身级别)进行碎片整理;这就是为什么
    free()
    通常执行得很差的原因

考虑到平均每个
调用上都有相当大的1KB堆栈帧(如果应用程序开发人员没有经验,可能会发生这种情况)10MB的堆栈对于10240嵌套的
调用就足够了。顺便说一句,除此之外,每个线程几乎不需要一个以上的堆栈和堆。

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

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

堆管理器最重要的工作是防止碎片。最好的方法是创建一个按大小对堆请求进行分区的查找列表。小于8字节的所有内容都来自第一个段列表。第二个段列表为8到16字节,第三个段列表为16到32字节,等等。随着大小的增加,桶的大小也会增加。您将为了获得最佳平衡,必须考虑存储桶大小。非常大的分配直接来自VM地址管理器

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


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

但线程之间的切换不应切换整个地址空间(并导致TLB刷新),因此,对于进程使用的每个线程,其堆栈都必须存在于进程的地址空间中。我文章中的链接显示了CLR进程如何具有很多堆。因此,需要在一个地址空间中有多个堆栈和堆。地址空间是一个非常不同的抽象级别。实际上,在这种情况下,您可以在同一个地址空间中创建这些多个堆栈和堆。地址空间本身由操作系统管理,而堆不是;它由用户级库代码管理。您很好地描述了堆的工作原理,但我不知道这个“方案”如何允许我创建任意数量的堆。如果我在使用后立即为第一个堆保留16 MBr的代码和数据,那么我应该把第二个堆放在哪里?就在第一个堆之后?那么第一个堆不能超过它最初的16MB。或者在Expansion上分割该堆