提问人:Blank 提问时间:10/13/2008 最后编辑:trincotBlank 更新时间:6/13/2021 访问量:5550
除了 malloc/free 之外,程序还需要操作系统提供其他任何东西吗?
Other than malloc/free does a program need the OS to provide anything else?
问:
我正在努力为我正在开发的操作系统设计内核(我实际上将其称为“核心”只是为了不同,但它基本上相同)。如果我无法启动和运行多任务处理、内存管理和其他基本功能,那么操作系统本身的细节就无关紧要了,所以我需要先处理一下。我有一些关于设计 malloc 例程的 questinos。
我认为 malloc() 要么是内核本身的一部分(我倾向于这一点),要么是程序的一部分,但无论哪种方式,我都必须编写自己的 C 标准库实现,所以我可以编写一个 malloc。在这方面,我的问题实际上相当简单,C(或 C++)如何管理其堆?
在理论课上,我一直被教导的是,堆是一个不断扩展的内存块,从一个指定的地址开始,在很多意义上表现得像一个堆栈。这样,我知道在全局范围内声明的变量处于开头,并且更多的变量在各自的作用域中声明时被“推送”到堆上,超出范围的变量只是留在内存空间中,但该空间被标记为空闲,因此堆可以在需要时扩展更多。
我需要知道的是,C 到底是如何以这种方式处理动态扩展堆的?编译的 C 程序是否对 malloc 例程进行自己的调用并处理自己的堆,或者我是否需要为其提供自动扩展的空间?另外,C 程序如何知道堆从哪里开始?
哦,我知道同样的概念适用于其他语言,但我希望任何示例都是 C/C++,因为我对这种语言最熟悉。我也不想担心其他事情,比如堆栈,因为我认为我能够自己处理这样的事情。
所以我想我真正的问题是,除了 malloc/free(它自己处理获取和释放页面等)之外,程序是否需要操作系统提供其他任何东西?
谢谢!
编辑我更感兴趣的是 C 如何使用 malloc 与堆的关系,而不是 malloc 例程本身的实际工作。如果有帮助,我在 x86 上执行此操作,但 C 是交叉编译器,所以没关系。^_^
进一步编辑:我知道我可能会混淆术语。我被告知,“堆”是程序存储全局/局部变量等内容的地方。我习惯于在汇编编程中处理“堆栈”,我刚刚意识到我可能是这个意思。我的一些研究表明,“堆”更常用于指代程序为自己分配的总内存,或者操作系统提供的内存页总数(和顺序)。
那么,考虑到这一点,我该如何处理不断扩大的堆栈呢?(看来我的 C 理论课有点......有缺陷。
答:
通常,C 库会根据需要处理 的实现,从操作系统请求内存(通过匿名或在旧系统中,)。因此,您的内核方面应该通过其中一种方式来处理整个页面的分配。malloc
mmap
sbrk
然后,要以一种不会过多地碎片化可用内存的方式分配内存。不过,我对这方面的细节并不太了解;然而,竞技场这个词浮现在脑海中。如果我能找到参考资料,我会更新这篇文章。malloc
有多种方法可以解决这个问题。
大多数情况下,C 程序有自己的 malloc/free 功能。那个适用于小物体。最初(一旦内存耗尽),内存管理器将要求操作系统提供更多内存。执行此操作的传统方法是 unix 变体上的 mmap 和 sbrk(Win32 上的 GlobalAlloc / LocalAlloc)。
我建议你从内存提供者(例如操作系统)的角度来看一下 Doug Lea 内存分配器(谷歌:dlmalloc)。该分配器是一流的,并且具有适用于所有主要操作系统的钩子。如果你想知道高性能分配器对操作系统的期望,这是你的首选。
评论
malloc
通常在用户空间的 C 运行时中实现,依赖于特定的操作系统系统调用来映射虚拟内存页。and 的工作是管理这些大小固定(通常为 4 KB,但有时更大)的内存页,并将它们切成应用程序可以使用的碎片。malloc
free
例如,参见 GNU libc 实现。
对于更简单的实现,请查看去年的 MIT 操作系统类。具体而言,请参阅最终的实验室讲义,并查看 。此代码使用类中开发的操作系统 JOS。它的工作方式是读取页表(由操作系统提供只读),查找未映射的虚拟地址范围。然后,它使用 and 系统调用将页面映射和取消映射到当前进程中。lib/malloc.c
sys_page_alloc
sys_page_unmap
您是否混淆了堆和堆栈?
我之所以问,是因为你提到了“一个不断扩展的内存”,在声明变量时,在堆上确定和推送变量的范围。这听起来确实像是在谈论堆栈。
在最常见的 C 实现中,自动变量的声明,例如
int i;
通常会导致 I 被分配到堆栈上。一般来说,除非你显式调用它,或者你所做的某个库调用调用它,否则 malloc 不会参与其中。
我建议阅读 Peter Van Der Linden 的“Expert C Programming”,了解 C 程序通常如何使用堆栈和堆的背景知识。
评论
必读:Knuth - 计算机编程艺术,第 1 卷,第 2 章,第 2.5 节。否则,您可以阅读 Kernighan & Ritchie 的“The C Programming Language”来了解实现;或者,您可以阅读 Plauger 的“标准 C 库”来查看另一个实现。
我相信,你需要在核心内部做的事情会与核心外部的程序所看到的有所不同。特别是,程序的核心内存分配将处理虚拟内存等,而代码外部的程序只看到内核提供的结果。
阅读有关虚拟内存管理(分页)的信息。它高度特定于 CPU,每个操作系统都专门为每个受支持的 CPU 实现 VM 管理。如果您正在为 x86/amd64 编写操作系统,请阅读其各自的手册。
危险危险!!如果你甚至考虑尝试内核开发,你应该非常清楚你的资源成本和它们相对有限的可用性......
关于递归的一件事是,它非常非常昂贵(至少在内核领域),你不会看到许多函数被写成简单地继续不受限制,否则你的内核会恐慌。
为了强调我在这里的观点,(在 stackoverflow.com 呵呵),请查看 NT 调试博客中关于内核堆栈溢出的这篇文章,具体来说,
·在基于 x86 的平台上, 内核模式堆栈为 12K。
·在基于 x64 的平台上, 内核模式堆栈为 24K。(基于 x64 平台包括具有 使用 AMD64 的处理器 架构和处理器使用 Intel EM64T 架构)。
·在基于 Itanium 的平台上, 内核模式堆栈为 32K,后备存储为 32K。
这真的,不是很多;
通常的嫌疑人
1.自由使用堆栈。
2.递归调用函数。
如果你稍微阅读一下博客,你就会发现内核开发是多么困难,有一组相当独特的问题。你的理论课没有错,很简单,很简单。;)
从理论上讲,>内核开发与上下文切换一样重要(也许在混合中保存一些虚拟机管理程序交互!
无论如何,永远不要假设、验证和测试你的期望。
评论