除了malloc / free之外,程序还需要操作系统提供其他任何东西吗?

时间:2008-10-12 21:32:20

标签: operating-system kernel malloc heap osdev

我正在设计内核(我将实际上称之为“核心”只是为了与众不同,但它基本相同)对于我正在研究的操作系统。如果我无法完成多任务,内存管理和其他基本操作,那么操作系统本身的细节就无关紧要了,所以我需要先解决这个问题。我有一些关于设计malloc例程的任务。

我认为malloc()要么是内核本身的一部分(我倾向于这个)还是程序的一部分,但是我将不得不编写自己的C实现标准库的方式,所以我得写一个malloc。在这方面,我的问题实际上相当简单,C(或C ++)如何管理它的堆?

我在理论类中一直被教导的是,堆是一个不断扩展的内存块,从指定的地址开始,并且在很多方面表现得像堆栈。通过这种方式,我知道在全局范围内声明的变量在开头,并且更多的变量被“推”到堆上,因为它们在各自的范围内声明,而超出范围的变量只是留在内存空间中,但是该空间被标记为空闲,因此如果需要,堆可以扩展更多。

我需要知道的是,C实际上如何以这种方式处理动态扩展堆?编译的C程序是否自己调用malloc例程并处理自己的堆,还是需要为它提供自动扩展的空间?另外,C程序如何知道堆的开始位置?

哦,我知道相同的概念适用于其他语言,但我希望任何示例都在C / C ++中,因为我对这种语言最为满意。我也不想担心其他事情,比如堆栈,因为我认为我可以自己处理这样的事情。

所以我想我的真正问题是,除了malloc / free(处理获取和释放页面本身等)之外,程序是否需要操作系统提供其他任何内容?

谢谢!

编辑我更感兴趣的是C如何使用malloc与堆相关而不是在malloc例程本身的实际工作中。如果它有帮助,我在x86上这样做,但C是交叉编译器所以它应该没关系。 ^ _ ^

进一步编辑:我知道我可能会让术语混乱。我被教导说“堆”是程序存储诸如全局/局部变量之类的东西。我习惯于在汇编编程中处理“堆栈”,我只是意识到我可能意味着相反。对我的一点研究表明,“堆”更常用于指代程序为自己分配的总内存,或者操作系统提供的内存页面的总数(和顺序)。

因此,考虑到这一点,我如何处理不断扩展的堆栈? (看来我的C理论课有点......有缺陷。)

7 个答案:

答案 0 :(得分:15)

malloc通常在用户空间的C运行时中实现,依赖于特定的OS系统调用来映射虚拟内存的页面。 mallocfree的工作是管理那些大小固定的内存页面(通常为4 KB,但有时更大),并将它们切片并切成应用程序可以使用的部分。

例如,请参见GNU libc实施。

要实现更简单的实现,请查看去年的MIT operating systems课程。具体来说,请参阅final lab handout,然后查看lib/malloc.c。此代码使用类中开发的操作系统JOS。它的工作方式是读取页面表(由OS提供只读),查找未映射的虚拟地址范围。然后,它使用sys_page_allocsys_page_unmap系统调用将页面映射和取消映射到当前进程。

答案 1 :(得分:13)

有多种方法可以解决这个问题。

大多数情况下,C程序都有自己的malloc / free功能。那个适用于小物件。最初(并且一旦内存耗尽),内存管理器将要求操作系统获得更多内存。执行此操作的传统方法是unix变体上的mmap和sbrk(Win32上的GlobalAlloc / LocalAlloc)。

我建议你从内存提供商(例如操作系统)的角度来看一下Doug Lea memory allocator(google:dlmalloc)。该分配器在一个非常好的分配器中是顶级的,并且具有所有主要操作系统的钩子。如果您想知道高性能分配器对操作系统的期望,那么代码就是您的首选。

答案 2 :(得分:4)

您是否混淆了堆和堆栈?

我问,因为你提到“一个不断扩展的内存片段”,范围和在声明它们时在堆上推送变量。这听起来好像你在谈论堆栈。

在最常见的C实现中声明自动变量,如

int i;

通常会导致我被分配到堆栈中。通常,malloc不会涉及,除非您显式调用它,或者您调用它的某些库调用。

我建议看看Peter Van Der Linden的“Expert C Programming”,了解C程序通常如何处理堆栈和堆栈。

答案 3 :(得分:1)

必读:Knuth - 计算机程序设计,第1卷,第2章,第2.5节。否则,你可以阅读Kernighan& Ritchie“C编程语言”看一个实现;或者,您可以阅读Plauger“标准C库”以查看其他实现。

我相信你在核心内部需要做的事情会与核心以外的程序有所不同。特别是,程序的内核内存分配将处理虚拟内存等,而代码外的程序只能看到内核提供的结果。

答案 4 :(得分:1)

了解虚拟内存管理(分页)。它具有高度CPU特性,每个操作系统都专门为每个支持的CPU实施VM管理。如果您正在为x86 / amd64编写操作系统,请阅读各自的手册。

答案 5 :(得分:0)

通常,C库处理malloc的实现,根据需要从操作系统请求内存(通过匿名mmap或在旧系统中sbrk)。所以你的内核方面应该处理通过类似其中一种方式分配整个页面。

然后由malloc以一种不会过多地分割空闲内存的方式发出内存。尽管如此,我并不是很喜欢 au fait 。然而,脑海中会出现 arena 这个词。如果我可以搜索引用,我会更新这篇文章。

答案 6 :(得分:0)

危险危险!!如果你甚至考虑尝试内核开发,你应该非常了解资源的成本以及相对有限的可用性......

关于递归的一件事是,它非常昂贵(至少在内核中),你不会看到许多函数被编写为简单地继续无关,否则你的内核会感到恐慌。

在此强调我的观点,(在stackoverflow.com heh),请查看NT Debugging blog关于内核堆栈溢出的帖子,具体来说,

  

·在基于x86的平台上,   内核模式堆栈 12K

     

·在基于x64的平台上,   内核模式堆栈 24K 。 (基于x64   平台包括系统   处理器使用AMD64   使用的架构和处理器   英特尔EM64T架构)。

     

·在基于Itanium的平台上,   内核模式堆栈 32K 32K   支持商店。

这真的,不是很多;

  

常见嫌疑人

     
     

1。大量使用堆栈。

     

2。递归调用函数。

如果您稍微阅读一下博客,您会看到一系列相当独特的问题,内核开发有多么艰难。你的理论课没错,简单,简单。 ;)

从理论出发 - >内核开发与上下文交换机一样重要(可能会在混合中保存一些虚拟机管理程序交互!!)。

无论如何,永远不要假设,验证和测试你的期望。