主篇:
开机后计算机会先进行加电自检,然后选择启动盘。然后计算机会检查启动盘的0面0磁道1扇区,如果这个扇区以0xAA55结尾的话。它将被当成一个引导扇区(它应当少于512字节),BIOS发现这个引导扇区后,就会把它加载到内存的0000:7c00处。最后跳转到0000:7c00处把控制转给这个引导扇区。
然而这个引导扇区被限制在512字节内,所以这里就需要一个Loader。引导扇区从磁盘的根目录区中找到Loader的位置,然后从该位置读取Loader将这个Loader装入内存,并把控制权移交纵给这个Loader,如此我们的代码就脱离了512字节的限制了。
系统刚开始处于实模式状态,为了处理32位的系统,Loader实现了从实模式进入保护模式的跳转和分页机制等。具体实现了以下几个步骤:
1、 初始化GDT
2、 初始化页目录表、页表、
3、 CR0启动分页机制,CR3指定页目录表
4、 加载GDTR
5、 打开A20地址线
6、 置CR0的PE位为1
7、 刷新CS寄存器(因为CS寄存器不能直接填充,所以只能从保护模式的32位代码跳转到16位代码由CPU自动去刷新CS寄存器的高速缓冲寄存器.)
最后Loader实现了加载内核,内核文件是一个可执行链接文件(linux是ELF格式的文件,window下是PE文件),这样就可以使用C编译器来编写内核。
内核复制Loader里的GDT到新的位置,增加中断处理和TSS,
1、 复制GDT新位置
2、 初始化中断控制器
3、 初始化IDT
4、 初始化TSS
5、 加载IDT
6、 LTR加载TSS描述符
多进程
在单CPU下当发生时钟中断时,中断处理程序会把控制权交给进程调度模块,进程调度模块判断是否要进行进程切换,当判断为要时就保存当前进程的状态到进程表,队列中的下一个进程被恢复执行。在windows中进程的实现是使用分页来实现的,当执行可执行链接文件时,每个进程分配页目录表和页表。
附录:
GDT(全局描述表)
在16位的实模式中,物理地址的计算方式为:
物理地址=段值*16+偏移
在32位的保护模式中,仍然使用(段值:偏移)的格式,但这个段值不是用来表示地址的。
它只是个索引,这索引指向一个数据结构中的一个表项(叫做描述符),表项中定义了段的起始地址、界限、属性等。而这个数据结构我们就叫它GDT(有可能还是LDT)。
结构如下图所示:
Selector(选择子)
它的结构如下图:
可以看到,当TI和RPL都为0时,选择子就是一个索引值了,而这个索引值表示为描述符相对于GDT基值的偏移。所以有时候选择值就等于一个偏移值。
GDTR寄存器(全局描述符表寄存器)
操作系统使用的,存放全局描述符表的地址。
结构如下图:
可以使用汇编指令lgdt [地址]来加载gdtr。
A20地址线
在8086时代,段:偏移的表示可以表示10FFEFh大小的内存,但8086只有20位地址总线,只能寻址1MB,那么当防问超过1MB时会怎样呢?这时候系统不会发生异常,而是回卷,重新从地址0开始寻址。但到了80286时,地址总线就没了限制了,这时候为了兼容以前的,就设置了一个8042键盘来控制第20位地址,也就是A20地址线。默认它是关闭的(等于0),也就是说默认需要回卷。
CR0寄存器
结构如下图:
控制寄存器CR0中的位0用PE标记,位31用PG标记,这两个位控制分段和分页管理机制的操作,所以把它们称为保护控制位。PE控制分段管理机制。 PE=0,处理器运行于实模式;PE=1,处理器运行于保护方式。PG控制分页管理机制。PG=0,禁用分页管理机制,此时分段管理机制产生的线性地址直接作为物理地址使 用;PG=1,启用分页管理机制,此时线性地址经分页管理机制转换位物理地址。由此可知,如果要启用分页机制,那么PE和PG标志都要置位。
LDT(局部描述表)
它的作用跟GDT差不多,LDT中的描述符同GDT中的描述符结构一样。不过LDT中的TI位置为1,选择子中的TI位也是用来区分GDT和LDT的位。
LDTR(局部描述寄存器)
使用lldt来加载,它的操作数为一个GDT中的选择子。
实现LDT。具体步骤如下:
1、 增加一个32位的代码段(作为LDT中的代码段)
2、 在GDT中增加一个用来描述LDT的描述符
3、 增加一个LDT段
4、 初始化LDT在GDT中的描述符
5、 初始化LDT中的描述符
6、 用lldt加载LDTR
7、 跳入局部任务(也就是LDT中的代码段)
IDT(中断描述符表)
8259A(中断控制器)
CPL(Current Privilege Level)、DPL(Descriptor Privilege Level)、RPL(Requested Privilege Level)
1、CPL是当前的程序或任务的特权级,它被存储在CS和SS的第0位和第1位。当程序转移到不同特权级的代码段时,处理器将改变CPL,但当程序访问一个与CPL特权级不同的一致性代码时,CPL不发生改变。
2、DPL表示段或门的特权级。当当前代码访问一个段时,DPL会与CPL和RPL比较,根据DPL所在特权级的段或门类型,处理器会进行不同的处理。
数据段:只有比这个DPL特权级高时,才可以访问这个数据段。
非一致代码段:同这个DPL相等的特权级才可以访问。
调用门:与数据段一样。
一致代码段和通过调用门访问的非一致代码段:只有比这个DPL特权级低时,才可以访问这个代码段。
TSS:与数据段一样。
3、RPL位于段选择子的第0位和第1位,处理器通过检查CPL和RPL来确认其访问时否合法。
门描述符
门描述符也是一种找述符,不过它与一般描述符的结构不一样,它的结构如下图所示:
门描述符可分为以下几类:
调用门(Call gates)
中断门(Interrupt gates)
陷阱门(Trap gates)
任务门(Task gates)
TSS
当代码从低特权级跳转到高特权级时,堆栈会发生改变,特权级别只有4个等级,所以每个任务最多会有4个堆栈。但是我们只有1个esp和1个ss寄存器,不够保存4个堆栈,这时候就需要用到TSS了,TSS是一个数据结构,它的结构如下:
我们这里要用到只有从偏移第4位到第27位的。当从Ring3跳到Ring1时,堆栈将被切换到esp1和ss1指定的位置,并把原堆栈内的内容复制到此堆栈中
分页机制
分页机制的开启开关位于CR0控制寄存器的最高位PG位,PG=1开启。
分页开启时地址的计算
以下是分页机制图表示:
如果觉得图不够明白的话,我们可以这样认为,分页机制就像我们的书本一样,
页目录表相当于目录,而目录有1024个子目录(相当于页表),每个项又有1024个项,每个项对应一个页码(相当于物理页首地址),每页有4KB个字(相当于线性地址后12位所指位置)。总之线性地址要转换成物理地址就是找到物理页的首地址加上线性地址的后12位。
实现分页机制的步聚如下:
1、 准备好页目录表和页表
2、 将CR3指向页目录表
3、 开启CRO的PG
系统调用
当用户进程需要使用高特权级内存和特殊指令时,这时候就需要用到系统调用,
它是应用程序和系统的桥梁,就像windows里使用的API来与操作系统进行通信。