《The Art of Linux Kernel》- Ch1
版本:Linux 0.11
硬件:IA-32 CPU、16MB 内存(2MB作为虚拟内存)、BIOS中设置软驱作为启动
(软盘相当于一种可移动的外部存储设备,由于读取速度慢、容量小等原因,目前已经被其他更高效、容量更大的外部存储设备所替代)
通电 -> main
通电->BIOS启动->内存中加载中断向量表、中断服务程序->32位模式
1、BIOS启动
找到BIOS
- CPU硬件逻辑实现:CS=0xF000 IF=0xFFF0
- BIOS的地址位置:0xFFFF0,固化在主机版的ROM中
BIOS加载中断向量表、中断服务程序
- 启动BIOS以后,开始监测显卡、内存等等,最重要开始加载中断向量表、中断服务程序
- 每个中断向量都指向对应的中断服务程序(实模式的中断机制)
- 256个中断向量,每个4字节,2字节IP的值,2字节是CS的值
2、加载操作系统内核程序
分批从软盘的不同扇区加载所有的操作系统程序到内存,为保护模式准备
加载第一批内核代码:bootsect
- 硬件设计+BIOS,出现一个int 0x19中断,对应找到对应的中断服务程序,这个服务程序的作用是将第一扇区的程序加载
- 第一扇区(boot sector)内对应的程序就是引导程序:bootsect(512B)
- 至此,从启动电脑终于接触到了linux操作系统自己的代码(一段汇编),加载到了内存中
讨论:对于不同操作系统,如何同样方式加载?
BIOS是写在主机板ROM上的,不管操作系统是啥,对于不同的操作系统类型,采用同样方式加载:
- BIOS接到启动os的指定,固定从启动扇区把程序加载到0x07C00
- 操作系统的第一段程序固定放在软盘0盘面0磁道1扇区
第二部分内核代码:setup
boosect的任务是继续加载第二、三批的内核代码,采取以下步骤:
(1)规划内存(key in os)
- 确保不同的程序、数据不会重叠,有足够的空间安身立命
(2)复制bootsect到新的位置
-
现在CPU的CS指向的就是BOOTSEG
-
bootsect把自身从BOOTSEG复制到INITSEG(pool)
1
2
3
4
5
6
7
8
9
10
11start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256 (256字就是512B)
sub si,si
sub di,di
rep
movw
-
bootsect直接可以跳转到INITSEG执行了(相当于原来的BOOTSEG位置是约定的位置,不是自己规划的位置),这个时候开始完全摆脱BIOS,根据自己的安排执行
1 |
|
- 代码整体位置变了,所以代码各个段(寄存器)也会发生变化
- SS(Stack segment):指向栈段
- SP(Stack pointer):栈顶指针
(3)加载setup程序
- int 0x13中断,指向磁盘服务程序
- 和加载bootect时候的0x19中断不同,0x19对应的服务程序是BIOS执行的,但现在是bootsect执行0x13指向的程序
- 0x19对应的服务程序写死,将第一扇区的程序加载到0x07C00,而0x13的程序可以指定扇区的代码加载到指定位置,开始要传参了
-
注意此时已经加载5个扇区,且setup.s会加载到INITSEG的位置
加载第三部分内核代码:system模块
-
仍然用0x13的中断
-
这次加载240个扇区,由bootsect的read_it完成,加载到SYSSEG之后
Bootsect收尾工作:检查根设备号
- 检查是否设置了根文件系统设备,若没有设置根据不同扇区数设置
- 所以linux启动要系统内核镜像、根文件系统
- 可以理解到这里,bootsect程序执行完了,接下来是setup的执行
setup执行
- jump 0,SETUPSEG,跳转到setup程序的位置开始执行
- 利用BIOS的中断服务程序,提取内核运行需要的机器系统数据:光标位置、显示页面等等,加载到内存对应的位置
- 加载的位置覆盖了bootsect的程序位置(只留出一个字节空间)
- 至此,内核代码加载完全结束,通过已经加载到内存的内核代码,系统将从实模式转变为保护模式
3、开启32位保护模式
废除原本的中断机制
- 关闭中断:关闭直到建立main适合的新的中断服务建立
- 将CPU的标识寄存器EFLAGS的IF置为0(中断不被允许)
- setup.s中的cli和sti分别是关、开中断
- 移动内核代码到0x00000,覆盖了原来BIOS的中断向量表、数据的区域
- 16位的实模式中断不再适合即将开始的32位linux中断,因此可以回收这块区域了
描述符表的准备
-
全局描述符表:GDT
- 唯一存段寄存器内容(段描述符)的数组,配合程序实现段寻址
- 所有进程的全局目录,存储每一个任务的局部描述符表(LDT)地址、任务状态段(TSS)地址
- 功能:各段的寻址、保护和恢复现场
- GDTR:GDT基地址寄存器,存GDT的基地址,通过LGDTR命令来加载GDT到GDTR
-
中断描述符表:IDT
- 所有中断服务程序的入口地址,类似实模式的中断向量表
- IDTR:IDT基地址寄存器
-
比较16位中断和32位中断
- 16位的中断向量表固定写死在0x00000的位置,32位利用IDT可以任意放位置,用IDTR来寻址
- 在中断重启之前,IDT是一张空的表
打开32位寻址
-
打开A20地址线,注意物理内存寻址和内存寻址的变化
-
内存寻址:直接从5个f变成8个f,32位,4GB的寻址空间
-
物理寻址:16MB(pool)
重新编程8259A
- 8259A是一个可编程的中断控制器
- 保护模式下,Intel保留了0x00-0x1F作为不可屏蔽中断、异常中断,所以要重新分布中断号
- CR0寄存器置为1,CR0寄存器保存的是PE(protected mode enable,保护模式使能,为0则实模式)
- 正式开启保护模式,对比开启前后的寻址变化(pool,P26)
4、 head.s的执行
p27
review
实模式和保护模式
- 实模式,即程序中用到的地址都是真实的物理地址,“段基址:段内偏移地址”产生的逻辑地址就是物理地址,即程序员可见的地址完全是真实的内存地址
- 保护模式:随着CPU的位数和寄存器的位数增加,有更安全内存地址定位方式。见详细解释
CS和IP
- CS是代码段寄存器,定义存放代码的存储器的起始地址
- IP为指令指针寄存器,他们一起合作指向了CPU当前要读取的指令地址
段寄存器
- pool
《The Art of Linux Kernel》- Ch1
https://al-377.github.io/2023/09/22/The-Art-of-Linux-Kernel-Ch1/