从加电到执行main函数之前的过程
1.1:启动BIOS,准备实模式下的中断性量表和中断服务程序
BIOS:加载软盘中的操作系统
BIOS由0xFFFF0开始执行
启动时,CPU的硬件加电,进入16位实模式状态运行,并强行把CS置为0xF000 ,IP置为0xFFF0 此时, CS:IP : 0xFFFF0
RAM:随机存取存储器 ROM:只读存储器
BIOS在内存中加载中断向量表和中断服务程序
=============
1.2加载操作系统内核程序并为保护模式做准备
@加载第一部分代码:引导程序bootsect
BIOS找到软盘.并加载到第一扇区
第一扇区内容:操作系统的引导程序
@加载第二部分代码:setup
bootsect对内存的规划:
SETUPLEN = 4
BOOTSEG = 0x07c0
INITSEG = 0x9000
SETUPSEG = 0x9020
SYSSEG = 0x1000
ENDSEG = SYSSEG + SYSSIZE
操作系统的设计者要全面,整体的考虑内存的规划
复制bootsect:
bootsect启动程序将它自身(512B)从内存0x07C00(BOOTSEG)处复制到内存0x90000(INITSEG)处. 说明操作系统开始根据自己需要安排内存了.
复制到新位置后,执行:
rep
movw
jmpi go, INITSEG
go: mov ax,cs
mov ds,ax
巧妙的实现了到新位置后,接着原来的执行顺序继续执行下去
改变了CS后,现在对DS,ES,SS,SP进行调整,栈操作指令标志着程序可以执行更为复杂的数据运算类指令.
栈操作是有方向的,由高地址到低地址方向
将setup程序加载到内存中
借助BIOS提供的int 0x13中断向量所指向的磁盘服务程序来完成.
int 0x19与int 0x13
19:有BIOS执行;13:由bootsect执行
19:加载第一扇区代码到0x07C00位置;13:根据设计者意图,把指定扇区的代码加载到内存的指定位置.所以需要事先传参
参数传递完毕后,执行int 0x13指令,产生13号中断,执行相应的中断服务程序,将软盘从0扇面0磁道第二扇区开始的4个扇区,即setup.s对应的程序加载到内存SETUPSEG(0x90200)处.所以bootsect与setup是连在一起的.
现在操作系统已经从软盘中加载了5个扇区的代码
@加载第三部分代码:system模块
bootsect借助BIOS中断int 0x13,将240个扇区的system模块加载进内存.加载工作主要由bootsect调用read_it子程序完成的,这个子程序将软盘第6扇区开始的约240个扇区的system模块加载至内存的SYSSEG(0x10000)处往后的120KB空间中.
此时main函数还没有开始执行
此时整个操作系统代码已全部载入内存.bootsect主体工作已经做完了,还有就是要再次确定一下根设备号.经一系列检测,得知软盘为根设备,所以把根设备号保存在root_dev中,这个根设备号作为机器系统数据之一
根文件系统设备:其他文件挂在在其上
机器系统数据:所占内存空间为0x90000~0x901FD.510个字节,在空间上,正好占一个扇区,在时间上,启动扇区的bootsect程序刚结束其使命,执行setup时立即就将其用数据覆盖,内存的使用率极高
========================================
1.3开始向32位模式转变,为main函数的调用做准备
在本节中,操作系统执行的操作包括:打开32位的寻址空间打开保护模式,建立保护模式下的中断相应机制等与保护模式配套的相关工作建立内存的分页机制,以及做好调用main函数的准备
@关中断并将system移动到内存地址起始位置0x00000
先关闭中断,即将CPU的标志寄存器(EFLAGS)中的中断允许标志IF置为0.直到下一章main函数中能够适应保护模式的中断服务体系被重建完毕才会打开中断,而此时响应的不再是BIOS提供的中断服务程序,而是系统自身提供的中断服务程序
代码为:
//代码路径:boot/setup.s
cli
关中断(cli)和开中断(sti)操作在操作系统代码中频繁出现.保护一个新的计算机生命的完整创建
下面,setup程序将位于0x10000的内核程序拷贝到内存地址起始位置0x00000处
0x00000原本存放着BIOS建立的中断向量表及BIOS数据区,这个复制动作将BIOS中断向量表和BIOS数据区完全覆盖,直到新的中断服务体系构建完毕之前,操作系统不再具备响应并处理中断的能力.
@设置中断描述符表和全局描述符表
setup继续为保护模式做准备,此时要通过setup程序自身提供的数据信息对中断描述符表寄存器IDTR和全局描述符表寄存器GDTR进行初始化设置
GDT:全局描述符表,它是系统中唯一存放段寄存器内容(段描述符)的数组,配合程序进行保护模式下的段寻址.可理解为所有进程的总目录表,其中存放着每一个任务局部描述符表(LDT)地址和任务状态段(TSS)地址,用于完成进程中各段的寻址,现场保护,与现场恢复
GDTR:GDT基地址寄存器
IDT:中断描述符表,保存保护模式下的所有中断服务程序的入口地址.类似于实模式下的中断向量表
IDTR:IDT基地址寄存器,保存IDT的起始位置
32位的中断机制和16位的中断机制在原理上有很大差别,16位的中断机制用的是中断向量表,中断向量表的起始位置在0x00000处,这个位置是固定的,32位的中断机制用的是中断描述符表IDT,位置是不固定的,可以灵活设置,再由IDTR寄存器来锁定其位置
创建这两个表的过程可分为两步进行:
1)在设计内核代码时,已经将两个表写好,并且把需要的数据也写好
2)将专用寄存器(IDTR,GDTR)指向表
在内存中做出数据的方法:
1)划分一块内存区域并初始化数据,"看住"这块内存区域,使之能被找到
2)由代码做出数据,如用push代码压栈,"做出"数据,此时采用的是第一种方法
@打开A20,实现32位寻址
打开A20,意味着CPU可以进行32位寻址,最大寻址空间为4GB. 从0xFFFFF到0xFFFFFFFF.
实模式下,当程序寻址超过0xFFFFF时,CPU将"回滚"至内存地址起始处寻址
@为在保护模式下执行head.s做准备
为了建立保护模式下的中断机制,setup程序将对可编程中断控制器8259A进行重新编程
8259A中断控制器:8259A是专门对8259A和8086/8088进行中断控制而设计的芯片,它是可以用程序控制的中断控制器.
在保护模式下,int 0x00~int 0x1F背Intel保留作为内部(不可屏蔽)中断和异常中断,如果不对8259A重新编程,int 0x00~ int0x1F中断将被覆盖.必须对其对应的中断号重新分布,即在保护模式下,IQQ0x00 ~ IRQ0x0F的中断号是int 0x20 ~ 0x2F.
setup程序通过下面代码的前两行将CPU的工作方式设为保护模式.将CR寄存器的第0位(PE)置为1,即设定处理器的工作方式为保护模式.
CR0寄存器:0号32位控制寄存器,存放系统控制标志.第0位为PE(保护模式使能)标志,置为1时CPU工作在保护模式下,置为0为实模式
//代码路径:boot;setup.s
mov ax,#0x0001
lmsw ax
jmpi 0,8
CPU工作方式转变为保护模式,一个重要的特征就是要根据GDT表来决定后续将执行哪里的程序
//
jmpi 0,8
'0'是段内偏移地址,'8'是保护模式下的段选择器,即1000
1000的最后两位00表示内核特权级,与之相对的特权级为11,第三位0表示GDT表,如果是1,则表示LDT.1000的1表示所选的表的1项(0项,1项,2项,也就是第2项)来确定代码段的段基址和段限长等信息此时,代码是从段基址0x00000000,偏移为0处开始执行的,也就是head程序的开始位置,这意味着执行head程序
到此为止,setup就执行完毕了,它为系统能在保护模式下运行做了一系列的准备工作,后续的准备将由head程序完成
@head.s开始执行
在执行main函数之前,先要执行三个由汇编代码生成的的程序,即bootsect,setup,和head.之后才执行由main函数开始的用C语言编写的操作系统内核程序
先将head.s汇编成目标代码,将用C语言编写的内核程序编译成目标代码,然后链接成system模块,也就是说,system

浙公网安备 33010602011771号