程序是记录在载体上的数据和指令。
程序正在执行时的一个副本叫做任务
所有段描述符都放在GDT --> 不做区分。
内核程序(任务)所占段在GDT中,用户程序(任务)所占段在LDT中 --> 做区分。
每个任务都有自己独立的LDT,也用来存放描述符。
需要跟踪所有任务的LDT,也需要一个特定寄存器LDTR(类似GDTR),LDT不止一个,多任务系统中会有很多个任务在CPU上流转,而某一时刻只有一个任务在CPU上工作,这个任务被称为“当前任务” Current Task。LDTR就指向当前任务的LDT,每当任务切换,新的任务“上台“,LDTR就转而指向它。
以前只有一个GDT,现在多了LDT,CPU该执行哪一个呢?取决于段选择子的TI位:
TI = 1 从当前任务的LDT中加载描述符
TI = 0 从GDT中加载描述符
以前整个机器上只有内核程序和一个用户程序的时候,没有任务切换的说法,任务不会中途被CPU切出,转而去执行另一个程序。现代多任务操作系统,在任务切出时上一个任务的”现场“需要保存,以便切回到它时CPU能够知道上一次执行它时执行到哪儿了,是个什么样的状态。核心需求就是”保存“这个动作。保存到哪儿?如何保存?这是亟待解决的问题。
每个任务都有一个LDT,为了解决切换任务时保护现场的问题,TSS(Task State Segment)任务状态段,用来保存任务切换时的状态,切换时任务就像被静止了一样,所有状态,包括以下图中的信息,都需要保存在TSS中,在切换回来时再从TSS中读取恢复。
和LDT一样,TSS也需要一个特殊寄存器去指向它,也就是TR。
多任务操作系统中,操作系统要肩负着任务的创建,以及在任务之间调度和切换的工作。不过,更为繁重和基础的工作是对处理器、设备及存储器的管理。对不同事件的响应,有些可以由任务来处理,有些只能被操作系统去处理,比如中断,内存分配,内存回收,设备访问,以及进程的排队与调度。
从程序员的角度来看,任务的全局空间包含了操作系统的段,是由别人编写的,但是他可以调用这些段的代码,或者获取这些段中的数据;任务局部空间的内容是由程序员自己创建的。通常,任务会在自己的局部空间运行,当它需要操作系统提供的服务时,转入全局空间执行。
由于这个特性,一个任务既能在其自己的局部空间执行,也可以到全局空间去执行。这就导致它可以访问的空间更加大了。
每个段描述符都对应着一个内存段。很显然,在一个任务的全局地址空间上,可以划分出2^13 个段,也就是8192 个段。因为GDT 的0 号描述符不能使用,故实际上是8191 个段,但这无关紧要。又因为段内偏移是32 位的,段的长度最大的4GB,因此,一个任务的全局地址空间,
其总大小为2^13 × 2 ^32 =245 字节,即32TB。同样的道理,局部描述符表LDT 可以定义2 ^13 个,也就是8192 个描述符,每个段的最大长度也是4GB,故,一个任务的局部地址空间为2 ^13×2 ^32 =245 字节,同样是32TB。这样一来,每个任务的总地址空间为2 ^45 +2 ^45 =2 ^45 ×2=2 ^45 ×21 = 2 ^46 字节,即64TB。
特权级保护
特权级(Privilege Level)
存在于描述符 以及 其选择子中的一个数值。
Intel 处理器可以识别4 个特权级别,分别是0 到3,较大的数值意味着较低的特权级别,反之亦然。如图14-4 所示,这是Intel 处理器所提供的4 级环状保护结构。
在上面描述符选择子的图片中最低两位是RPL,request privilege level
在下面这张段描述符的图片中高32位14 13位是DPL。descriptor privilege level
2个bit位的表示范围是:0,1,2,3
数值越低 级别越高
DPL 决定了访问其所在描述符所应当具备的最低特权级别。
当处理器正在一个代码段中取指令和执行指令时,那个代码段的特权级叫做当前特权级(Current Privilege Level,CPL)
一般情况下:
范围 | 特权级 |
---|---|
操作系统 | 0 |
驱动程序 | 1 or 2 |
用户程序 | 3 |
一般来说,操作系统是最先从BIOS 那里接收处理器控制权的,进入保护模式的工作也是由它做的,而且,最重要的是,它还肩负着整个计算机系统的管理工作,所以,它必须工作在0 特权级别上,当操作系统的代码正在执行时,当前特权级CPL 就是0
当任务在局部空间运行时(CPL = 3),想要访问硬件资源,这个时候就需要操作系统介入(操作系统拥有最高级别)(CPL = 0)
一些权力至上,比如停机指令hlt 和对控制寄存器CR0 的写操作,像这样的指令只能由最高特权级别的程序来做。因此,那些只有在当前特权级CPL 为0 时才能执行的指令,称为特权指令(Privileged Instructions)型的特权指令包括加载全局描述符表的指令lgdt(它在实模式下也可执行)、加载局部描述符表的指令lldt、加载任务寄存器的指令ltr、读写控制寄存器的mov 指令、停机指令hlt 等十几条。
想要从一个特权级的代码段,跳转到另一个特权级的代码段,需要遵循一些原则和方法
- 一般来说控制转移只允许发生在两个特权级相同的代码段之间。
- 低特权级要跳转到高特权级有两种办法:高特权级代码设置为依从 或是 使用 门描述符
依从
简单来说就是让一个高特权级的段,设置为服从于当前正在执行的低特权级的段。当低特权级的段
必须满足 CPL>= 目标代码段描述符的DPL
就可以让控制转移到依从的代码段上执行,但是执行时并不使用依从代码段的DPL,而是使用调用放的特权级
门
门(Gate)是另一种形式的描述符,称为门描述符,简称门。
和段描述符不同,段描述符用于描述内存段,门描述符则用于描述可执行的代码,比如一段程序、一个过程(例程)或者一个任务
门描述符 描述了 目标过程(例程)所在代码段的选择子,以及段内偏移
不同门的作用分类:
门类型 | 作用 |
---|---|
不同特权级之间切换 | 调用门 |
中断处理过程 | 中断门/陷阱门 |
任务切换 | 任务门 |
要想通过调用门进行控制转移,可以使用jmp far 或者call far 指令,并把调用门描述符的选择子作为操作数。
使用jmp far 或者 call far指令 来调用门进行转移 参数为门描述符所在的选择子。
不同之处:
jmp far | call far | |
---|---|---|
特权级变化 | 不改变CPL | CPL被赋值为目标代码段DPL |
不允许从特权级高的代码段将控制转移到特权级低的代码段,因为操作系统不会引用可靠性比自己低的代码。
不管是实施控制转移(jmp和call)还是访问数据段,都可以看作一个请求。请求者通过提供一个段选择子来请求访问指定段。此处的请求使用的就是RPL请求者特权级别
在绝大多数时候,请求者都是当前程序自己(程序自己提供了选择子),因此,CPL=RPL。
判断请求者是谁最简单的方法是看谁提供了选择子
有些情况下CPL != RPL特权级为3 的应用程序希望从硬盘读一个扇区,并传送到自己的数据段,因此,数据段描述符的DPL 同样会是3。由于I/O 特权级的限制,应用程序无法自己访问硬盘。好在位于0 特权级的操作系统提供了相应的例程,但必须通过调用门才能使用,因为特权级间的控制转移必须通过门。
假设,通过调用门使用操作系统例程时,必须传入3 个参数,分别是CX 寄存器中的数据段选择子、EBX 寄存器中的段内偏移,以及EAX 中的逻辑扇区号。
高特权级别的程序可以访问低特权级别的数据段,这是没有问题的。因此,操作系统例程会用传入的数据段选择子代入段寄存器,以便代替应用程序访问那个段:mov ds,cx