天天看点

Linux0.11内核--启动引导过程

<!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } -->

 启动搬迁过程:

  1、BIOS将磁盘引导块程序bootsect读入到内存0x7c00,开始执行指令;

  2、bootsect将自己搬迁到内存0x90000,跳到该段中的自己的下一条指令执行;

  3、bootsect将设备检测安装程序setup读入到0x90200;

  4、bootsect将内核映像system读入到0x10000,跳到setup头部0x90200执行指令;

  5、setup获取系统参数,依次保存在0x90000;

  6、setup将system从0x10000搬迁到0x0000;

  7、setup设置硬件寄存器和CPU状态字寄存器,进入32位保护模式,跳到system头部0x0000执行指令。

  8、开始执行内核映像中的指令。

  9、内核映像首部的指令(head.s)设置各个寄存器,加载中断描述符表、全局描述符表,检测芯片,

   对16M内存进行分页。

  10、执行main程序。

  

  

  阅读三个汇编程序和注释,很快就能了解到启动引导程序在进入main函数前作了这些事情。

  为什么要这样搬迁和执行呢?

  

  简单地讲,内核启动搬迁过程是:bios装载运行bootsect,bootsect装载setup、system,运行setup

  搬迁system。bios的行为是已经固定写好烧在主板上bios芯片里的了。不是linus所控制的,也不是

  操作系统的范畴了。bios应该是和硬件架构紧密相关的。所以为什么bios将bootsect读到0x7c00这么

  个怪地址,也没有其它的好讲了————对架构我不懂。 :( 

  在加载bootsect之前,bios从0地址开始加载了中断向量表————这个是我们的汇编代码中可以使用bios中

  断功能的基础。是在实模式中我们的原始武器和工具。按照一个中断向量占四个字节,7c00前面如果都是

  中断向量表的话,这里应该有7c00/4 = 7936个中断向量了。应该没有这么多吧? 可能是有空余空间供

  扩展? 不得而知。

  

  bootsect自己搬迁自己的行为,我没有找到充分的理由。

  setup被装载到0x90200,system被装载到0x10000,这些都没有覆盖到0x7c00+512的空间,也就不会造成

  自己装载的咚咚覆盖了自己的指令代码的危险。而这个bootsect的512字节大小,至今也没有变化,也不

  应该是为后续扩展所保留的。从Linus讲setup直接load到0x90200可以看出,这个512字节也应该是不会

  变大的————由于bootsect是由bios装载的,变大了说明bios的代码和行为都变化了,这个是不现实的。

  所以我能解释的理由就是:linus觉得bios加载的地方不爽,所以特意把它挪到0x90000和setup放在一起。

  如果你知道这个bootsect搬迁的理由,请不吝告诉我~ 

  

  

  关于setup

  setup被bootsect装载到了0x90200,bootsect执行完之后就执行了setup代码。setup被装载到0x90200,

  而不是其它的地方,应该是由于system占用了0x10000~0x90000,而bootsect占用了0x90000~0x90200。

  由于0x7c00+512地址以前的地方被bios占用了。因此setup可能的装载的地方有:0x7c00+512后面,

  或者0x90200后面。而在setup代码的执行过程中有一个动作,将system从0x10000搬迁到0地址。这样,

  由于预留的512K大小的system必然把0~0x80000的内存都覆盖了。也就是说如果把setup装载在0x7e00

  之后,则会发生自己的搬迁行为修改了自己的指令地址————这是不可以的。 

  而且将setup装载在0x7e00,则setup的大小受到限制,不能超过0x7e00到0x10000这块地址。

  setup获取的设备信息覆盖了0x90000的bootsect的代码。然后将system向0地址平移。这个动作覆盖了

  系统中断和0x7c00的bootsect。

  这个时候为什么可以覆盖中断向量表呢? 我想是基于这么几个原因:

  setup通过bios提供的这些工具能获取的系统参数都已经获取并保存了,工具已经没用了。在后面进入

  保护模式后,也已经不能通过中断向量的方式来使用bios中断了。前一个原因可以通过查看后面的汇编

  代码得到证实:后面的代码都没有再调用bios中断。第二个原因可以通过后续代码的动作和注释得到

  证实:setup位32位保护模式加载了全局描述符表,中断描述符表。 后续发生中断时,将通过中断描述

  符表中的项映射到中断处理代码上去。这里面原来中断向量的每一项应该是四个字节(我没有记错的话),

  而中断描述符表中的项有八个字节————32位保护模式下,包含的中断信息更多了。 

  

  关于system

  system就是Linux的内核了。内核首部的head.s的指令其实应该还是算作引导程序。放在内核中,而不是

  像setup一样单独编出来,估计是为了后续版本更方便的修改吧————毕竟内存分页、GDT、IDT之类的东西

  修改的可能性是很大的。 

  为什么将system从0x10000搬迁到0地址,书上也说了,好处是对于其中的代码,线形地址和物理地址是一样

  的,这样子写程序更方便一些————linus这样说。实际上,我感觉不搬迁也没有什么不行。代码和数据的操作

  也应该没有什么不方便,毕竟就是汇编里面也是使用的标号。或许还是我没有理解到?

  system加载了全局描述符表,中断描述符表。中断描述符表已经说过了,用来映射中断服务程序和中断号的。

  全局描述符表,则是用来描述全局符号的。呵呵,这个解释。全局描述符表是一个记录各个任务的相应段信息

  的全局表。表中两个字作为一个任务的描述项:局部描述符表LDT,任务状态段TSS。局部描述符表指向某个

  具体任务的各个段信息(代码段,数据&堆栈段)的结构。任务状态段TSS应该是用来记录任务状态进行任务调度

  用的吧? 从这里看来,多进程的实现应该就是保存各个任务的上下文,进行切换和上下文处理吧。而上下文,

  也就是寄存器状态、指令位置、数据状态了。 

  head.s还对16M内存进行了分页。每4096字节的内存分为一页,用四个字节描述一页(应该是四个字节的32位地址

  指向这一页的首部吧)。然后1024个页地址组成一个页表,一个页表占据一页(1024×4=4096)。一个页表也就

  管理了1024×4096=4M内存。Linus的16M内存即占用了四个页表来描述。然后四个页表的地址又写到0地址开始的

  地方,占用了16字节,形成了页目录了。 页目录从0地址开始,页表从0x1000开始。也即第0页都是页目录了。

  按照Linus的这个处理,4096/4 × 4M = 4G,也就是这个页目录的大小可以管理4G内存了。不过如果真的初始化

  了4G的内存页,会出问题。 4G内存需要1024个页表,一个页目录,供占用1025个页面,这样页表就写过了

  0x90000,要写到1025×1024 = 0x101000,比0x90200超过很远了————后果就是现在写到0x90200+0x400×2=

  0x90a00后面的全局描述符表和中断描述符表了。中断描述符表无所谓,反正是哑中断。但是描述符表被破坏了,

  后面的就没得玩了。

  好了,全局描述符表(多任务的容器)建好了,内存分页也做好了。 就等main来打展身手了。

继续阅读