2011-06-03 35 views
5

当初始化我的内核时,我有一些需要发生的事情:1)需要启用分页,2)物理内存管理器需要从grub解析内存映射,3)配套启动代码需要访问需要留在那里的数据(例如GDT,IDT,内存管理结构)。更高一半的内核初始化

这些步骤之间的依赖关系让我疯狂。在更高一半的情况下,内核链接到它的虚拟地址,所以我提出的选项是1)在汇编中启用分页,这将涉及所有多重启动指针(汇编中),以便它们仍然可以访问到物理内存管理器,然后将其全部取消映射,2)将启动代码链接到其物理地址,然后执行一些指针操作来访问其物理地址处的内核结构,或者3)不使用更高一半的核心。

还涉及在编译时不知道物理内存量的情况下引导物理内存管理器。我非常肯定,在分配第一个结构时,我必须小心避免所有的多引导结构,或者先使用它们,然后不要担心覆盖它们(尽管我仍然需要处理模块,并且这种方法很可能涉及将多重引导表复制到已知位置,因为我在设置物理内存管理器时需要它们)。

这些问题是为什么我避免了更高的半内核到现在。有没有人有解决这些依赖关系的好系统?可能在this GDT trick上有一些变化来访问其链接/虚拟地址处的内核以及物理地址处的多重引导表,或者使用某种预先定义的页表避免上述问题,可能涉及PSE?

回答

4

我这是怎么解决这个问题:

我的内核映像是通过在GRUB(物理)地址为0x01000000(16MB,只是ISA DMA区域上方)加载。该图像基本上由两部分组成:

  1. “early init”部分。本节包含为准备跳转到更高一半内核而执行的代码。我还在本节中预留了一些空间,以便在此准备过程中使用堆栈和堆。本节中的所有代码都在(虚拟)地址0x01000000处链接。
  2. 图像的其余部分包含代码和数据,它们是高内核的一部分。此部分中的所有代码都以(虚拟)地址0xc0000000(3GB)链接。

由于init初期部分的代码与加载的地址链接在相同的地址,因此GRUB可以毫无问题地跳转到该代码中。此早期init代码执行以下步骤:

  1. 重新定位GRUB传递给内核的MBI结构。早期init部分内部的堆用于此目的。
  2. 标识将从物理地址0x0开始的所有页面映射到早期init节使用的最后一页的物理地址。身份映射意味着虚拟地址与物理地址相同。这可以确保启用分页后仍可以执行初始init部分中的代码。
  3. 在虚拟地址0xc0000000处映射较高的一半内核。
  4. 启用分页。
  5. 跳转到高半内核。

在这一点上,其余的初始化是从更高一半的代码完成的。这包括设置GDT,IDT,内存管理,......请注意,MBI将重新定位到一个众所周知的位置,所以您不必担心用自己的数据结构覆盖它。

有关物理内存管理器的一小部分内容:我所做的是计算我的数据结构所需的页面数量,从内核映像之后的第一页开始分配这些结构,并在这些数据结构之后开始处理页面。

我希望这个解释清楚。如果不是,请让我知道。如果你愿意的话,我也可以为你提供一份我的内核的副本。

+0

谢谢!一个问题 - 初始化堆(我猜测)包含重新定位的MBI,当你完成它时可以放弃,但它听起来像它也包含初始映射的页表。一旦你创建了第一个过程,你是否也放弃了这些? – rpjohnst 2011-06-03 12:21:11

+0

@Rusky:是的,MBI可能会被丢弃,但我不这样做,因为它太小了。我不把页面目录和页面表放入堆中,而是放在非常低的内存中(大约4KB)。我不会放弃这些,因为我运行的第一个流程会继承它们。即我的第一个进程将在我初始化期间设置的地址空间中运行。 – Job 2011-06-03 13:58:43