xv6系统引导过程分析

我们知道bootloader主要完成下列主要任务:

  • 切换进保护模式
  • 加载内核
  • 转移控制权

Bootloader往往需要处理不同的文件系统来加载内核。有时甚至需要支持网络加载。它可以变得很复杂,以至于成为一个微型操作系统。这样的Bootload很显然不能屈身与磁盘引导区512字节中,需要采用二级加载的形式:一级为loader of bootloader,二级为loader of kernel(也就是bootloader)。

xv6的bootloader为一级加载,因为它特别简单,可以在510个字节内实现。比如xv6简化了内核寻找的过程:内核以ELF形式连续存放在磁盘上,没有涉及文件系统的操作。虽为一级加载,但xv6的bootloader却分为两个部分来实现:汇编(bootasm.S)和C语言(bootmain.c)。前者完成切换保护模式的任务,同时初始化C语言运行时。后者完成读取磁盘扇区寻找并加载内核的过程。

xv6的引导过程相比于我的season确实优雅 =^=。season在一个汇编文件中完成了所有的切换模式、加载内核、转移控制、准备C运行时的任务。season的bootloader和xv6的唯一一致的是偷懒的思想:快速进入C语言,别在汇编上折腾了。不过season借助外部工具能够快速加载kernel,切进C语言就运行kernel。xv6进入C语言后去加载kernel,不借助外部工具,而且灵活性强,显然要比season优雅。

加载内核后,bootload将控制权交给内核,具体说是跳转到内核的entry函数。在entry中xv6打开了4MB页扩展,并设置了页表——将0~4MB和KERNBASE~KERNBASE+4MB的虚拟地址全部映射成0~4MB的物理地址。这个页表只是为了让内核访问4MB存储空间暂时设定的页表。后面会用成熟的页表对其进行替换。毕竟0~4MB的虚拟地址是属于进程地址空间的用户空间。

最后开启分页并进行一次跳转激活分页。跳转的目的地就是main()函数。