x86的启动
进入BIOS
x86在上电启动后,和所有处理器一样,执行一条固定位置的指令。其位置是CS=FFFF000H,EIP=0000FFF0H。此时运行在实模式下,因此CS:EIP得到的即为物理地址(详见80386实模式)。该合成的地址为FFFFFFF0H,即EPROM所在的地址。该位置通常是一条jmp长跳转指令,跳转到BIOS中运行。
x86架构下各型号CPU上电后寄存器初始值
进入BootLoader
CPU进入BIOS执行后,BIOS负责加载存储设备(如硬盘,或U盘)上的第一个扇区,即通常说的启动引导扇区MBR。MBR中存储了操作系统的Bootloader代码。这段代码会被加载到内存的0x7c00处,随后CPU跳转到这个地址开始执行。
BootLoader做的事
BootLoader中的代码负责使能CPU的段机制,并使CPU进入保护模式。然后从指定的硬盘扇区(不一定是第一扇区)读取操作系统kernel到内存,跳转到kernel的入口位置,将CPU交给操作系统。
这样做的目的
从启动到操作系统开始运行,进行了两次跳转,经过了BIOS和BootLoader。这样做的目的,对BIOS来说是根据CPU来进行系统的初始化,使操作系统不需要专门适配每个型号的CPU。对于BootLoader来说,能够从不同的扇区加载操作系统。
/***************************************************/
x86的段机制
Intel CPU保护模式和实模式的不同,很重要的一点就是段机制的引入。在实模式中, CS:IP两个寄存器,就指定了程序代码的物理地址,CPU直接跳转到那里执行。在保护模式中, 虽然最终地址的合成是一样的(但x86地址位数为32位),但是合成之前,需要经过全局描述符表GDT。GDT事实上是一个段表,即一系列基地址组成的列表。GDT的每一项称为段描述符,其中存放着一个段的索引,段起始地址和段大小信息。在这种情况下,CS中存放的不再是基址,而是13位索引,又称段选择子。根据这个索引,找到相应的段描述符,取出段起始地址作为基址,再加上EIP寄存器中的偏移量,合成线性地址。在没有页机制的情况下,该线性地址就成为寻址的物理地址。
GDT由操作系统建立,其表头地址保存在GDTR寄存器中。
/**************************************************/
x86的中断
x86对于突发事件有三种定义:中断,异常和系统调用。三种事件的发生条件和处理不同,但中间的传递机制大同小异。
与简单微控制器的中断方式一样,x86中断也通过中断号和中断向量表,来查询对应的中断服务例程。但x86在得到中断向量后,需要查询的是中断描述符表IDT。IDT的每一项中存放了一个段选择子和一个偏移量。通过中断向量从IDT中找出该向量对应的段选择子,再跳转到GDT里查得对应的基址,和上述IDT里的偏移量组成该中断向量对应的中断服务例程入口地址。
进入中断服务例程前,CPU要先将重要的寄存器压栈。对于内核请求的中断,主要是CS,EIP,EFLAG(中断标志),ERRORCODE(异常代码)。对于用户应用程序请求的中断,还需压入SS和ESP,因为此时涉及堆栈上下文的切换(内核是不会与用户应用使用同一个堆栈的)。
执行完中断服务例程后,使用iret指令弹出上述寄存器的值。