Q1:CPU为什么要切换特权级?
可执行程序的代码可分成两部分
(1)3特权级的用户程序
(2)0特权级的内核程序
在执行可执行程序时,需要在这两种代码间进行切换。但是OS内核代码的特权级高于用户程序,因此需要切换特权级。
Q2:执行高特权级代码的方式?
(1)利用一致性代码段,这种方式可以让指令指针跳转到高特权级的代码段,但是并不会改变当前的CPL,不会提高CPU特权级。
(2)利用系统调用,通过门结构提高CPU的特权级,这也是唯一提高CPU特权级的方式,共有4种门结构,中断门,调用门,任务门,陷阱门,现代操作系统通常使用中断门来实现系统调用
Q3:系统调用的分类:
任务门和调用门都可以通过call和jmp指令直接调用,其中call指令可实现向高特权级代码段转移,jmp只能平级转移。
Q4:从用户态到内核态的切换过程?
eflags寄存器:
关键位:
IF:限制外部设备的中断,避免中断嵌套
NT:任务嵌套标志位
TF:陷阱标志位,为0表示禁止单步执行
中断发生后NT和TF会被置0,如果对应的是中断门,IF也会被置0。
(一)向目标代码段转移
( 1 ) 将参数压入目前3特权级所在的栈中
( 2 ) 在门描述符中获取到转移目标代码段的DPL,如果DPL<CPL,说明目标代码段是特权级更高
如果是向更高的特权级转移:
( 3 )在TSS中找到更高特权级的栈段选择子和偏移,检查DPL和TYPE,如果未通过处理器引发异常
( 4 ) 将旧栈指针SS和ESP压入到新栈当中
( 5 ) 将原栈中压入的参数复制到新栈当中
( 6 ) 将原CS和IP压入到新栈当中
4,5,6步骤如下图A,B,C所示:
(7)将CS更新为门描述符中的代码段选择子,将EIP更新为门描述符中的偏移量
如果是平级转移:
(3)直接将CS和EIP压入当前3特权级的栈中
(二)从目标代码段返回
(1) 当在指令中遇到retf时,处理器对栈中CS的RPL与当前的CPL进行比较,判断是否需要改变处理器的特权级
(2) 对栈中的CS_old和EIP_old对应代码段的DPL进行特权级检查,检查通过将CS_old和EIP_old弹出恢复
(3) 如果特权级发生变化,再恢复SS和ESP
TSS:
存储任务环境的数据结构,包括任务在不同特权级下的栈地址(TSS只记录0, 1, 2特权级下的栈地址,因为处理器在向高特权级转移时,会自动将低特权级的栈指针压入高特权级所在的栈中),TR寄存器存储TSS在内存中的地址。