需要了解Linux0.11-的启动引导过程

嵌入式技术

1368人已加入

描述

启动搬迁过程:

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来打展身手了。



 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分