9

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

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

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

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

哦,我知道同样的概念也适用于其他语言,但我希望任何示例都使用 C/C++,因为我最熟悉那种语言。我也不想担心堆栈等其他事情,因为我认为我能够自己处理这样的事情。

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

谢谢!

编辑我对 C 如何使用与堆相关的 malloc 比对 malloc 例程本身的实际工作更感兴趣。如果有帮助,我会在 x86 上执行此操作,但 C 是交叉编译器,所以没关系。^_^

进一步编辑:我知道我可能会混淆术语。我被告知“堆”是程序存储诸如全局/局部变量之类的东西的地方。我习惯于在汇编编程中处理“堆栈”,我刚刚意识到我可能是这个意思。我的一些研究表明,“堆”更常用来指代程序为自己分配的总内存,或者操作系统提供的内存页的总数(和顺序)。

那么,考虑到这一点,我该如何处理不断扩大的堆栈?(看来我的 C 理论课有点……有缺陷。)

4

7 回答 7

16

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

例如,参见GNU libc实现。

如需更简单的实现,请查看去年的MIT 操作系统课程。具体来说,请参阅最终的实验室讲义,并查看lib/malloc.c. 这段代码使用了课堂上开发的操作系统JOS。它的工作方式是读取页表(由操作系统以只读方式提供),寻找未映射的虚拟地址范围。然后它使用sys_page_allocsys_page_unmap系统调用将页面映射和取消映射到当前进程。

于 2008-10-12T22:03:01.730 回答
14

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

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

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

于 2008-10-12T21:40:18.420 回答
4

您是否混淆了堆和堆栈?

我问是因为您提到了“一块不断扩展的内存”、作用域和在声明时将变量推送到堆上。这听起来确实像是您实际上在谈论堆栈。

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

诠释我;

通常会导致 i 被分配到堆栈上。通常 malloc 不会参与,除非您明确调用它,或者您进行的某些库调用会调用它。

我建议查看 Peter Van Der Linden 的“Expert C Programming”,了解 C 程序通常如何与堆栈和堆一起工作的背景知识。

于 2008-10-12T22:17:23.417 回答
1

必读:Knuth - Art of Computer Programming,第 1 卷,第 2 章,第 2.5 节。否则,您可以阅读 Kernighan & Ritchie “The C Programming Language” 来查看实现;或者,您可以阅读 Plauger "The Standard C Library" 来查看另一个实现。

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

于 2008-10-12T23:50:04.447 回答
1

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

于 2008-10-13T05:38:28.533 回答
0

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

然后malloc以一种不会过多地分割空闲内存的方式分配内存。不过,我对这件事的细节并不太熟悉;但是,我想到了竞技场一词。如果我能找到参考,我会更新这篇文章。

于 2008-10-12T21:38:04.120 回答
0

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

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

为了强调我的观点,(在 stackoverflow.com 上,呵呵),请查看NT Debugging 博客中关于内核堆栈溢出的这篇文章,特别是,

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

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

· 在基于 Itanium 的平台上,内核模式堆栈为32K,带有32K 后备存储。

真的,不是很多。

普通嫌疑犯


1. 自由使用堆栈。

2.递归调用函数。

如果您稍微阅读一下该博客,您会发现内核开发对于一组相当独特的问题是多么困难。你的理论课没有错,很简单,很简单。;)

从理论出发 -> 内核开发与上下文切换一样重要(也许在混合中保存一些管理程序交互!!)。

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

于 2009-07-14T06:40:36.803 回答