在制作操作系统的过程中,首先的第一步就是要加载内核,但是加载内核又涉及到一个很长的话题.
曾经做过一个FAT32文件系统下加载内核的一整段汇编代码,感觉到这样写代码万分有压力,而且这方面的东西,太过底层,甚至上说和操作系统没有什么直接的关系.
所以,为了简化这步骤,我们利用上一个很强大的加载器,grub2.
如何用grub2等引导程序去引导我们制作的内核呢?
这里给出一个答案:我们必须受到一些约束才能完成这些工作,关于这个约束就是multiboot规范(当然还有别的解决办法)
接下来,我们开始用grub2+multiboot来引导我们的简易操作系统内核.
首先,还是介绍一下大致流程做到心中有数,才能以不变应万变.
准备:
1.《multiboot规范》.pdf
2.一个简易的kernel
3.u盘或者是虚拟镜像
4.虚拟机或者实际机器自备
准备工作已经完成,既然我们用到 multiboot规范,我们就该大致了解一下这个规范到底给我们带来了什么.
简略介绍一下multiboot规范就是为了引导各种不同的操作系统核心而提出的一个规范,目的可以从名字看出多引导。
而grub2和grub都支持这个规范,所以我们可以利用他引导我们制作的OS内核.
流程: 开机->grub ->加载os内核到内存->跳到内核代码
知道流程后,我们快速解读一下multiboot规范
1. 引导头
2. 内核镜像
根据规范,我们可以看到我们只需提供一个数据结构(引导头),然后将内核文件编译内核镜像(这里实际上就是一个普通的应用程序),我们只需设置引导头中flage标志,便可以完成加载内核的功效,并且我们会得到引导器中返回我们需要的一些信息,比如(内存分配信息和引导介质的信息)。
这里grub帮助我们进入了保护模式,并且从返回的信息我们可以知道内存等信息,我们只需要设置一下GDT等,内核初始化工作就可以编写我们需要的内核了.
另外还有一个需要关注的信息就是,内核究竟被加载到哪里去了?
加载的过程和文件格式有关,比如windows pe,linux elf,都源自一种文件古老的coff结构,所以windows和linux的程序被加载的过程很像.
但是我们这里关系的只是加载到内存中的问题,这个话题有点跑远了, 规范中提到默认支持的程序加载格式有很多,我们需要提供一些参数才能顺利完成加载.为了方便起见我们最后用ELF文件格式,不需要做任何设置,只需在编译内核阶段制定一个被加载的虚拟地址即可.
因为分页机制没有被开启,我们的内核仅仅只完成了保护模式的跳转,我们虚拟地址就等于物理地址...于是,地址的问题解决了.
最后总结一下, 利用grub2+multiboot规范引导我们的内核文件,,,
我们关心三个问题:
1.如何让我们的内核镜像被 grub识别, 所以我们编译后内核镜像中必须包含这部分,加载器会自动识别,grub的要求引导头在内核镜像是逻辑地址1M之前,这个值当然是越小越好.
2.被加载到内存中内核的物理地址,这个地址就是该内核程序的虚拟地址,linux下的程序默认是0x84xxxx,我们可以通过改变链接器LD的选项来更换加载的地址.
3.完成加载后,我们将会得到的信息 在multiboot规范上都有介绍,如寄存器的初始值,和我们需要得到信息的地址,请仔细阅读.
这里理论上的准备应该就叙述完毕,下一篇我们就应该是制作部分了.