Linux0.11内核采用x86架构,作为学习操作系统的辅助材料,本次来探究Linux0.11版本的启动过程(从上电到System模块的执行)。
电源键,开启操作系统之路的大门
x86采用冯诺依曼体系,从上电的那一时刻开始,计算机通过cpu的CS段寄存器和IP寄存器指向操控计算机的第一道程序,这道程序便是BIOS(Basic Input Output System),被烧录在主板的非易失性ROM(Read Only Memory)芯片中。
上电后,CS = 0xFFFF, IP = 0x0000,通过硬件电路解释指向0xFFFF0处,进行硬件检测,后通过 int 0x19 中断将磁盘0磁道0扇区的bootsect.s加载存的0x7c00处,并跳转。
为什么是0x7c00
出于对早期计算机兼容性考虑,将bootsect.s写入到0x7c00处,即32K的最后1024个字节,其中bootsect.s占据了512个字节。至于为什么要放在尾部,以及剩余的512字节用来做了什么,以下转自网络转载结果Why BIOS loads MBR into 0x7C00 in x86 ? - Glamenv-Septzen.net(en)
BIOS developer team decided 0x7C00 because:
1. They wanted to leave as much room as possible for the OS to load itself within the 32KiB.
2. 8086/8088 used 0x0 - 0x3FF for interrupts vector, and BIOS data area was after it.
3. The boot sector was 512 bytes, and stack/data area for boot program needed more 512 bytes.
So, 0x7C00, the last 1024B of 32KiB was chosen.
为了最大限度的使用空间,将bootsect.s及数据文件共占用的1k字节空间放置在32k的最后,当此次数据使用过后,将该空间供给其他程序使用。
操作系统引导程序 bootsect.s
经由bios跳转至0x7c00处执行bootsect.s开始,该引导程序将写入0x7c00的数据搬入0x90000中并跳转,之后通过int13中断将第2-5扇区存放的setup.s程序读入内存(0x90200),紧跟着bootsect.s程序。之后加载Logo,并读入随后的操作系统主体-System模块。至此,bootsect.s程序大体任务结束,跳转至0x90200处执行setup.s
为什么将程序从0x7c00搬至0x90000处
在之后的操作中,setup.s会将操作系统整体搬至0x0000处,并保持不变,而操作系统本身的大小很大可能会超过32K(事实上已经超过32k了),若不将0x7c00处的程序搬移,而是继续在0x7c00后读入setup.s,那么搬移操作系统的过程中就可能会覆盖setup.s,导致电脑出现问题。
setup.s
程序开始执行,通过中断获取计算机的光标信息,内存信息,显卡信息等写入0x90000,覆盖bootsect.s(执行过后就不再使用了),初始化gdt表和idt表(此时对gdt表的初始化是为了进入保护模式后能正常跳转到0x0000处),为接下来进入保护模式进行准备,将操作系统搬至0x0000处,将cr0寄存器(控制地址解释和中断电路的寄存器)最后一位置1,进入保护模式,跳转到0x0000处,正式进入操作系统的System模块。
由汇编转向C语言:head.s
从0x0000处开始执行,首先执行head.s程序。head.s重新初始化gdt,idt表,并通过栈进入main.c程序,正式从汇编程序过渡到c程序。