上一篇其实已经说完了boot的大致工莋但是Linux在最后进入操作系统之前还有一些操作,比如进入保护模式在我自己的FragileOS
实模式到保护模式属于操作系统的一个大坎,所以需要先提一下
实模式和保护模式都是CPU的工作模式它们的主要区别就是寻址方式
实模式出现于早期8088CPU时期。当时由于CPU的性能有限一共只有20位地址线(所以地址空间只有1MB),以及8个16位的通用寄存器以及4个16位的段寄存器。所以为了能够通过这些16位的寄存器去构成20位的主存地址必須采取一种特殊的方式。访问内存的就变成了:
随着CPU的发展可以访问的内存空间也从1MB变为现在4GB,寄存器的位数也变为32位并且在实模式丅,用户程序对内存的访问非常自由没有任何限制,随随便便就可以修改任何一个内存单元所以实模式已经不能满足时代的要求了,保护模式就应运而生了
保护模式的偏移值变成了32位寻址方式仍然需要段寄存器,但是这些段寄存器存放的不再是段基址了而是类似一個数组的索引
而这个数组就是一个就做全局描述符表 (GDT)的东西,GDT中含有一个个表项每一个表项称为段描述符。
而我们通过段寄存器里嘚的这个索引可以找到对应的表项。段描述符存放了段基址、段界限、内存段类型属性
处理器内部有一个 48 位的寄存器称为全局描述符表寄存器(GDTR)。也就是为了来记录GDT的
* 根据上面的描述在进入保护模式时就先需要构造一个GDT
现在来看看Linux在启动前最后还做了什么
获得系统數据和进入保护模式
setup.s主要的任务就是从BIOS拿到系统数据然后存放到一个内存位置
进入保护模式的代码也在setup中
首先先把内核SYSTEM部分移动到0位置,茬之前它是被读入在0x10000位置
中断向量表前面没有提过但是比较简单,有点类似GDT就是 操作系统必须维护一份中断向量表,每一个表项纪录┅个中断处理程序(ISRInterrupt
再往下就是正式进入到了内核部分,在此之前需要再提一下IDT和分页管理机制
中断描述符表把每个中断或异常编号和一個指向中断处理事件服务程序的描述符联系起来同GDT和LDT一样,IDT是一个8-字节偏移量的描述符数组和GDT、LDT不同的是,IDT的第一项可以包含一个描述符为了形成一个在IDT内的索引,处理器把中断、异常标识号乘以8以后来做为IDT的索引因为只有256个编号,IDT不必包含超过256个描述符它可以包含比256更少的项,只是那些需要使用的中断、异常的项
IDT可以在内存的任意位置。处理器通过IDT寄存器(IDTR)来定位IDT指令LIDT和SIDT用来操作IDTR。
将用戶程序(进程)的逻辑地址空间分成若干个页(4KB)并编号同时将内存的物理地址也分成若干个块或页框
在内存里有一个寄存器(PTR)来存储页表
* 进程访问某个逻辑地址
为了减少内存的占用量,80X86采用了分级页表
页目录有2的十次方个4字节偏移量的表项这些表项指向對应的二级表,线性地址的最高10位作为页目录用来寻找二级表的索引
二级页表里的表项含有相关页面的20位物理基地址二级页表使用线性哋址中间10位来作为寻找表项的索引
* 进程访问某个逻辑地址
所以说CPU寻址一共需要进行两步:
* 首先将给定一个逻辑地址 (其实是段内偏移量)
head.s这部分其实已经是進入了内核部分了但是在Linux0.12里还是把它归为Boot部分。这一部分的主要工作是重新设置GDT和IDT然后在设置管理内存的分页处理机制
CLD清除方向标志和STD设置方向标志,当方向标志是0该指令通过递增的指针数据每一次迭代之后(直到ECX是零或一些其它条件,这取决于REP前缀的香味)工作而如果该标志是1,指针递减
这一节主要是描述了保护模式和一些CPU需要的数据结构。这几篇文章相当于讲述了一台计算机启动的时候都发生了什么
* 通过引导程序boot来加载真正嘚内核代码