Memory management 操作系统通常如何管理内核内存和页面处理?

Memory management 操作系统通常如何管理内核内存和页面处理?,memory-management,operating-system,kernel,paging,osdev,Memory Management,Operating System,Kernel,Paging,Osdev,我正在进行内核设计,我有一些关于分页的问题 到目前为止,我的基本想法是:每个程序都有自己的(或它认为的)4G内存,减去我为程序可以调用的内核函数保留的部分。因此,操作系统需要找出一些方法来将程序在运行期间需要使用的页面加载到内存中 现在,假设我们有无限多的内存和处理器时间,我可以加载/分配程序写入或读取的任何页面,使用不存在(或被交换掉)的页面的页面错误,以便操作系统可以快速分配或交换它们。但在现实世界中,我需要优化这个过程,这样我们就不会有一个程序不断地消耗它曾经接触过的所有内存 所以我想我的

我正在进行内核设计,我有一些关于分页的问题

到目前为止,我的基本想法是:每个程序都有自己的(或它认为的)4G内存,减去我为程序可以调用的内核函数保留的部分。因此,操作系统需要找出一些方法来将程序在运行期间需要使用的页面加载到内存中

现在,假设我们有无限多的内存和处理器时间,我可以加载/分配程序写入或读取的任何页面,使用不存在(或被交换掉)的页面的页面错误,以便操作系统可以快速分配或交换它们。但在现实世界中,我需要优化这个过程,这样我们就不会有一个程序不断地消耗它曾经接触过的所有内存

所以我想我的问题是,操作系统通常是如何做到这一点的?我最初的想法是创建一个函数,程序调用该函数来设置/释放页面,然后它可以自己管理内存,但程序通常会这样做,还是编译器假定它有空闲页面?此外,编译器如何处理需要分配相当大内存段的情况?我是否需要提供一个函数,尝试按顺序为其提供X个页面

这显然不是语言特定的问题,但我偏向于标准C,并且很好用C++,所以我希望任何代码示例都在那个或汇编中。(组装应该不是必需的,我完全打算让它尽可能多地使用C代码,并作为最后一步进行优化。)

另一件应该更容易回答的事情是:人们通常如何处理程序需要调用的内核函数?有一个包含程序可以调用的最基本的函数/进程特定内存的设置区域(我想在虚拟空间的末尾)可以吗?我的想法是,当程序需要做一些重要的事情时,让内核函数做一些非常奇特的事情并交换页面(这样程序就无法在自己的空间中看到敏感的内核函数),但我现在并不真正关注安全性

所以我想我更担心的是总体设计思路而不是细节。我想让内核与GCC完全兼容(以某种方式),我需要确保它提供了普通程序所需要的一切


感谢您的建议。

此问题的答案高度依赖于体系结构。我假设你说的是x86。对于x86,内核通常提供一组系统调用,它们是内核的预定入口点。用户代码只能在这些特定点进入内核,因此内核可以仔细控制它如何与用户代码交互

在x86中,有两种实现系统调用的方法:使用中断和syscenter/sysexit指令。对于中断,内核建立一个中断描述符表(IDT),它定义了内核中可能的入口点。然后,用户代码可以使用
int
指令生成软中断以调用内核。中断也可以由硬件产生(所谓的硬中断);这些中断通常应该不同于软中断,但它们不必如此

sysenter和sysexit指令是执行系统调用的一种更快的方式,因为处理中断的速度很慢;我不太熟悉使用它们,所以我不能评论它们是否适合您的情况

无论使用哪种接口,都必须定义系统调用接口。您可能希望在寄存器中而不是在堆栈上传递系统调用参数,因为生成中断将导致您将堆栈切换到内核堆栈。这意味着您几乎肯定必须在用户模式端和内核端编写一些汇编语言存根来进行系统调用,然后再在内核端收集系统调用参数并保存寄存器

一旦这些都准备好了,就可以开始考虑处理页面错误了。页面错误实际上只是另一种类型的中断-当用户代码试图访问没有页面表条目的虚拟地址时,它将生成中断14,并且您还将获得错误地址作为错误代码。内核可以获取这些信息,然后决定从磁盘读入丢失的页面,添加页面表映射,然后跳回用户代码


我强烈建议你看看课堂上的一些材料。查看参考资料部分,它有很多好东西。

所有这些问题的一个好的起点是了解Unix是如何做到这一点的。正如一句著名的名言所说,“那些不懂UNIX的人注定要拙劣地重新发明它。”

首先,关于调用内核函数。仅仅将函数放在程序可以调用的地方是不够的,因为程序很可能在“用户模式”(IA-32上的环3)下运行,内核必须在“内核模式”(IA-32上的环0)下运行才能执行其特权操作。您必须以某种方式在两种模式之间进行转换,这是非常特定于架构的

在IA-32上,传统的方法是在IDT中使用门和软件中断(Linux使用int 0x80)。较新的处理器有其他(更快的)方法来实现这一点,哪些方法可用取决于CPU是来自AMD还是Intel,以及具体的CPU型号。为了适应这种变化,最近的Linux内核在每个进程的地址空间顶部使用一页由内核映射的代码。因此,在最近的Linux上,要执行系统调用,您需要在此页面上调用一个函数,这将依次执行切换到内核模式所需的任何操作(内核具有该页面的多个副本,并选择