特权级与TSS
作者:互联网
整个计算机世界其实可以分为两个部分,访问者
和受访者
。访问者是动态的,具有能动性,它主动去访问各种资源。受访者是静态的,它就是被访问的资源,只能干坐着等待访问者光顾。访问者的特权级可变
,受访者的特权级不可变
。
CPU即是大脑,又是守卫,它负责维护计算机内的安全,将程序分为四个特权级,从高到低依次为0,1,2,3
级,数字越小,特权越大。0特权级是OS内核
所在的特权级,PC在启动之处就以0级运行,当MBR从BIOS那接完棒后,就像神一样处于最高特权级。
OS位于最内环的0级特权,它要直接控制硬件,掌控各种核心数据,权力最大。系统程序分别为于1级和2级,运行在这两层的一般是虚拟机
、驱动程序
等系统服务。在最外层的是3级特权级,我们的用户程序就运行在此层,它不需要太大的能力,权力最弱。当用户程序需要完成更深层的功能时,应当进行特权级跨越,进入系统服务层或OS内核来执行。
TSS简介
TSS,即Task State Segment
,意为任务状态段,和特权级有着密不可分的联系。TSS是处理器在硬件层面
上原生支持多任务
的一种数据结构,也就是说,CPU在硬件上就已经为OS提供了完成多任务的一种解决方法(不过后来OS并不买账)。
就像每个任务都有自己的LDT
一样,任何一个任务都有独自的TSS
,它用于标识一个任务并存储相关信息,相当于身份证,任务拥有此结构才能运行。注意,TSS是处理器硬件上用于任务管理的系统结构,处理器能够自动
识别其中每一个字段代表什么,因此开发者只需按意义指定每个字段的值即可,其结构为:
这个结构看上去很复杂,一共26个字段,104字节。实际上,104字节只是TSS的最小尺寸,按照需要还可以再接上个IO位图,不过这些现在不考虑。这里简单介绍一下第28字节及以上的一些字段:
cr3
字段同理cr3寄存器,指向该任务所用的页目录
基址。ESP
为该任务的堆栈段的栈顶指针,即该任务的LDT中的堆栈段的栈顶地址。CS
为该任务的代码段,即LDT中代码段的选择子
、SS
为该任务的堆栈段,即LDT中堆栈段的选择子
。顾名思义,LDT
就是该任务的LDT选择子
。这里给出一个简单的任务TSS示例:
[SECTION .TSSB]
ALIGN 32
[BITS 32]
LABEL_TSSB:
DD 0 ; Back
DD TopOfStackB ; 0 级堆栈
DD SelectorStackB ;
DD 0 ; 1 级堆栈
DD 0 ;
DD 0 ; 2 级堆栈
DD 0 ;
DD PageDirBase1 ; CR3
DD 0 ; EIP
DD 0 ; EFLAGS
DD 0 ; EAX
DD 0 ; ECX
DD 0 ; EDX
DD 0 ; EBX
DD TopOfStackLDTB ; ESP
DD 0 ; EBP
DD 0 ; ESI
DD 0 ; EDI
DD 0 ; ES
DD SelectorLDTBCodeB ; CS
DD SelectorLDTBStack ; SS
DD 0 ; DS
DD 0 ; FS
DD 0 ; GS
DD SelectorLDTB ; LDT
DW 0 ; 调试陷阱标志
DW $ - LABEL_TSSB + 2 ; I/O位图基址
DB 0ffh ; I/O位图结束标志
TSSBLen equ $ - LABEL_TSSB
而真正和特权级相关联的,是28字节一下的内容,即3个堆栈段选择子和3个栈偏移,这里着重介绍下这6个字段。
在没有操作系统的情况下,可以认为进程就是任务,任务就是一段在处理器上运行的程序,相当于某个计算机高手在能够脱离OS执行代码,直接控制硬件。但是有了OS之后,程序被分为了用户程序
和操作系统内核程序
,故,一个完整的任务也被分为用户部分
和内核部分
两部分。由于内核程序位于0特权级,用户程序位于3特权级,所以,一个任务按特权级来划分的话,实质上是被分成了3特权级的用户程序和0特权级的内核程序,这两部分加在一起才是能让处理器完整运行的程序,也就是说,完整的任务要经历这两种特权级的变化
。所以,我们平时在Linux下所写的程序只是个半成品,咱们只负责完成用户态下的部分,内核的部分由操作系统提供。
任务是由处理器执行的,任务在特权级变换时,本质上是处理器的当前特权级CPL
在变换,由一个特权级变成了另一个特权级,这就开始涉及栈的问题了。处理器固定,在不同的特权级下,应该用不同特权级的栈,原因是如果在同一个栈中容纳所有特权级的的数据时,这种交叉引用会使栈变得非常混乱。并且,用一个栈容纳多个特权级下的数据,栈容量有限,这很容易溢出。因此,一个任务在每个特权级下只能用该特权级的栈。
每个任务的每个特权级下只能有一个栈,也就是说,一共4个特权级,一个任务最多4个栈,那么为什么TSS中只有3对堆栈段选择子和栈偏移呢?要搞清楚这个问题,得先弄明白TSS中记录的3个栈是用来干什么的。
刚才说过,当处理器要进入不同的的特权级时,它自动在TSS中找到同特权级(目标特权级)的栈,由于TSS是硬件结构,所以处理器可以直接识别各字段的意义。
特权级转移分为两类:
1> 由中断门
、调用门
等手段实现低特权级向高特权级转移;
2> 由调用返回指令
从高特权级返回到低特权级,这是唯一能够处理器降低特权级的情况;
对于第1种,当处理器要从低向高跳转时,会提前将目标栈记录在某个地方,当实行跳转时再从中取出来加载到SS
和ESP
中以转换栈,TSS中的3对栈字段就是那个用来存放的地方。由于除了返回指令,处理器只能由低到高
跳转,所以TSS中所记录的栈是转移后的高特权级目标栈,所以它一定要比当前特权级要高。这样就明白了,TSS中不会记录第3特权级的栈,因为没有更低特权级向它转移。
对于第2种,由高特权级返回到低特权级,处理器已经不需要从TSS中找对应的目标栈了,因为此时要返回到的低特权级的地址其实已经被暂存起来了,这就是由转移指令(int
、call
)的机制来决定的,换句话说,处理器知道去哪里找低特权级的目标栈。
如上图所示,当处理器由低特权级向高特权级跳转时,会自动的当时低特权级的栈地址(ss
和esp
)压入了转移后的高特权级所在的栈中。当返回指令如retf
或iret
从高特权级向低特权级中返回时,处理器可以从当前使用的高特权级的栈中获取低特权级的栈段选择子以及偏移量。具体过程参考call-ret
的机制。
那处理器是怎么找到TSS的呢?
TSS是硬件支持的数据结构,它和GDT等一样,由软件填写其内容,由硬件使用。GDT要加载到寄存器GDTR
中才能处理器找到,TSS也一样,由TR (Task Register)
寄存器加载的,每次处理器执行不同任务时,将TR寄存器加载不同任务的TSS就行了。
标签:特权,DD,任务,处理器,堆栈,TSS 来源: https://blog.csdn.net/weixin_46322986/article/details/123164789