日期:2014-05-16  浏览次数:20692 次

Linux内核0.12——从一个简单的多任务内核实例学习保护模式

《Linux内核完全剖析——基于0.12内核》(赵炯著)P.140

/******引导启动程序boot.s******/

此程序仅能加载长度不超过16个扇区的head代码,并且直接使用BIOS默认设置的中断向量号
首先利用BIOS中断把内核代码加载到内存0x10000处,然后移动到内存0处
最后进入保护模式,并跳转到内存0开始处继续运行
把内核代码移动到物理内存0开始处的主要原因是为了设置GDT表时可以简单一些,但是我们不能直接将内核代码加载到内存0处,因为加载操作需要使用BIOS提供的中断过程,该中断向量表正处于内存0开始的地方

boot.s的流程如图:


说明如下:
1、加载内核代码到内存0x10000时,需要利用BIOS中断int 0x13功能2,DH为磁盘号,DL为驱动器号,CH为10位磁道号低8位,CL为位6、7磁道号高2位,ES:BX=0x1000:0为缓冲区位置,AH为读扇区功能号,AL为需读扇区数

2、IDT为中断描述符表,它将每个异常或中断向量分别与它们的处理过程相联系,IDT是由8字节长描述符组成的一个数组,最多可有256个中断描述符,IDT可以驻留在线性地址空间的任何地方,使用IDTR寄存器可以定位IDT的位置,IDT描述符分为中断门、陷阱门和任务门三种描述符,异常与中断处理的操作如图:


3、GDT为全局描述符表,当任务发生切换时,LDT会更换成新的LDT,但是GDT并不会改变,因此,GDT所映射到一半虚拟地址空间是系统中所有任务共有的,即系统中所有任务共享的段由GDT来映射,每个系统必须定义一个GDT,并可用于系统中所有程序或任务。GDT本身并不是一个段,而是线性地址空间中的一个数据结构,处理器并不使用GDT中的第一个描述符,GDT必须含有LDT的段描述符,即每个LDT都必须在GDT中有一个段描述符和段选择符,此引导程序中设置的GDT表长度为2KB,可容256个描述符项,线性基地址在0x7c0段的偏移gdt处。本程序共设置三个段描述符,段描述符0不用,段描述符1设置为:0x00c09a00000007ff,段描述符2设置为:0x00c09200000007ff,段描述符64位二进制的通用格式如图:

根据对各位的置数,可以将两个段描述符设置为代码描述符和数据描述符(此时S=1)

/××××××××多任务内核程序head.s××××××××/

head.s包含了32位保护模式初始化设置代码、时钟中断代码、系统调用中断代码和两个任务的代码,在初始化完成之后程序移动到任务0开始执行,并在时钟中断控制下进行任务0和1之间的切换操作,整个head.s的流程如图:


说明如下:

1、多任务的初始化:如果要使用多任务机制,软件初始化代码必须至少设置一个TSS及其相应的TSS段描述符,TSS描述符应和LDT段描述符一样存入GDT中,head.s中的任务0、1分别将其的TSS0、TSS1存入了GDT

2、段选择符:是段的一个16位标识符,段选择符指向段描述符表中定义段的段描述符,段选择符有三个字段:请求特权级、表指示标志、索引值,在head.s中,共有这么几个段选择符:屏幕显示内存段选择符0x18,任务0的TSS段选择符0x20,任务0的LDT段选择符0x28,任务1的TSS段选择符0x30,任务1的LDT段选择符0x38,任务0的LDT、TSS段选择符在执行任务0时的人工堆栈操作中将其加载到LDTR和任务寄存器TR中,屏幕显示内存段选择符在执行显示字符程序中存入GS寄存器中,任务1的LDT段选择符被存入其TSS段选择符中,在定时中断程序中进行TSS0和TSS1的切换