CPU上电之后将执行程序地址指向0xFFFF0处,这是一个纯硬件完成的动作,地址0xFFFF0的位置是BIOS程序的起始地址,也就是说,CPU上电之后,硬件引导执行BIOS程序。
BIOS程序被固化在计算机主板上的一块很小的ROM芯片里。BIOS程序在内存最开始的位置(0x00000)用1KB的内存空间构建中断向量表,在紧挨着它的位置用256字节的内存空间构建BIOS数据区,并在大约57KB以后的位置加载了8KB左右的与中断向量表相应的若干中断服务程序。
中断向量表中有256个中断向量,每个中断向量占4个字节,其中两个字节是CS的值,两个字节是IP的值,每个中断向量都指向一个具体的中断服务程序。
现在开始要执行真正的boot操作了,也就是将硬盘中的操作系统程序加载至内存。计算机分三批逐次加载操作系统的内核代码:第一批由BIOS中断int 0x19把第一扇区的bootsect的内容加载到内存;第二批和第三批在bootsect的指挥下,分别将其后的4个扇区和随后的240个扇区的内容加载至内存。
如何增加电脑内核、下面先介绍几个概念:
bootsect:引导程序,负责扇区存储内容的引导和加载。
Interrupt Vector Table:中断向量表,记录所有中断号对应的终端服务程序的内存地址。
Interrupt Service:中断服务,通过中断向量表的索引对中断进行响应服务。
BIOS程序启动之后,其要做的就是找到硬盘并加载第一扇区。int 0x19中断向量所指向的中断服务程序,即启动加载服务程序,就是我们所说的bootsect引导程序。第一扇区被称为启动扇区,第一扇区的载入,标志着Linux中的代码即将发挥作用。
BIOS已经把bootsect载入到内存了,现在它的作用就是把第二批和第三批程序陆续加载到内存中。bootsect要做以下三个步骤:
①规划内存
为了把第二批和第三批程序加载到内存中的适当位置,bootsect首先做的工作就是规划内存。
②复制bootsect
bootsect把自身的所有程序(512B)从0x07C00的位置复制到0x90000的位置。
③将setup程序加载到内存中
BIOS利用int 0x13中断向量所指向的终端服务程序来完成setup程序的加载。
这里说一下目前所用到的两个中断向量int 0x19和int 0x13的区别:
Ⅰ.int 0x19中断向量所指向的启动加载服务程序是BIOS执行的,而int 0x13中断服务程序是Linux操作系统自身的启动代码bootsect执行的;
Ⅱ.int 0x19的中断服务程序只负责把第一扇区的代码加载到0x7C00位置,而int 0x13的中断服务程序则不然,它可以根据设计者的意图,把指定扇区的代码加载到内存的指定位置。
BIOS借着BIOS中断 int 0x13,将240个扇区的system模块加载到内存。到此位置,第三批程序已经加载完毕,整个操作系统的代码已经全部加载至内存,bootsect的主要工作已经做完。
接下来setup程序开始执行。它做的第一件事情就是利用BIOS提供的中断服务程序从设备上提取内核运行所需要的机器系统数,其中包括光标位置、显示页面等数据,并分别从中断向量0x41和0x46向量值所指的内存地址处获取硬盘参数表。
到此为止,操作系统内核程序加载工作已完成。
之前操作系统是在16位实模式下工作的,接下来,操作系统要使计算机在32位保护模式下工作,这期间要做大量的重建工作,并且持续到操作系统的main函数执行,包括打开32位的寻址空间、打开保护模式、建立保护模式下的中断响应机制等于保护模式配套的相关工作、建立内存的分页机制,最后做好调用main函数的准备。
准备工作要先关闭中断,因为接下来的过程中,在main函数中能够适应保护模式的中断服务体系重建之前,系统都不应该收到中断源的影响。随后,setup程序将位于0x10000的内核程序复制到了内存地址起始位置0x00000处。这个复制动作将BIOS中断向量表和BIOS数据区完全覆盖,使它们不复存在,知道新的中断服务体系构建完毕之前,操作系统不再具备相应并处理中断的能力。
setup程序在完成3.1的步骤后,要通过自身的数据信息对中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR)进行初始化设置。下面介绍有关的几个概念:
① GDT:全局描述符表(Global Descripter Table),在系统中唯一存放段寄存器内容的数组。可理解为所有进程的总目录表,其中存放每一个任务(task)局部描述符表(LDT)地址和任务状态段地址,完成进程中各段的寻址、现场保护与现场恢复。
② GDTR:GDT基地址寄存器(Global Descripter Table Register),保存GDT的起始地址。
③ IDT:中断描述符表(Interrupt Descripter Table), 保存保护模式下所有中断服务程序的入口地址,类似于实模式下(1.2中)的中断向量表。
④ IDTR:IDT基地址寄存器(Interrupt Descripter Table Register),保存IDT的起始地址。
电脑开机main、打开A20,以意味着CPU可以进行32位寻址,最大寻址空间为4GB。内存范围从0xFFFFF变到0xFFFFFFFF,从1MB变为4GB。地址线从20根变成了32根。
为了建立保护模式下的中断机制,setup程序将对可编程中断控制器8259A进行重新编程。CPU在保护模式下,int 0x00~int 0x1F被Intel保留作为内部(不可屏蔽)中断和异常中断。如果不对8259A进行重新编程,int 0x00 ~ int 0x1F中断将被覆盖。
到这里,setup就执行完毕了。它为系统能够在保护模式下运行做一系列的准备工作。但这些准备工作还不够,后续的准备工作由head程序来完成。
在执行main函数之前,先要执行三个由汇编代码生成的程序,即bootsect,setup和head。之后才执行由main函数开始的C语言编写的操作系统内核程序。
根据前面的步骤,第一步,加载bootsect到0x7C00,然后复制到0x90000;第二步,加载setup到0x90200。这两段程序是分别加载,分别执行的。
head程序的加载方式是这样的:先将head.s汇编成目标代码,将用C语言编写的内核程序编译成目标代码,然后链接成system模块。也就是说,system模块里面既有内核程序,又有head程序,head程序在前,内核程序在后。所以head程序的名字为“head”。head程序在内存中占有25KB+184B的空间。根据3.1的内容,system模块加载到内存后,setup将system模块复制到0x00000位置,由于head程序在system的前面,所以实际上,head程序就在0x00000这个位置。head程序在内存中的分布示意图如下图所示:
head程序除了做一些调用main的准备工作之外,就是用程序自身的代码在程序所在的内存空间创建了内核分页机制,即在0x000000的位置创建了页目录表,页表,缓冲区,GDT,IDT,并将head程序已经执行过的代码所在的内存空间覆盖。这意味着head程序自己将自己废弃,main函数即将开始执行。
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态