5. 处理器在实施任务切换时的操作(任务切换总结)
处理器可以通过以下四种方法实施任务切换:
- call指令或者jmp指令的操作数是GDT内的某个TSS描述符;
- call指令或者jmp指令的操作数是GDT或者LDT内某个任务门描述符;
- 一个异常或者中断发生时,中断号指向IDT内的某个任务门;
- 在EFLAGS寄存器的NT位置位的情况下,当前任务执行了一个iret指令。
对于这四种方法,首先都可以得到一个TSS描述符的选择子:
- 对于1,指令的操作数直接就是TSS选择子;
- 对于2和3,任务门里包含了TSS选择子;
- 对于4,当前任务的TSS的任务链接域内就是TSS选择子。
这个TSS选择子就决定了要切换到哪个任务。
在任务切换时,处理器执行以下操作:
- 取得新任务的TSS描述符的选择子(如上文所述)。
- 检查是否允许从当前任务切换到新任务。
(1)数据访问的特权级检查规则适用于JMP和CALL指令,当前(旧)任务的CPL和新任务段选择子(TSS描述符的选择子或者任务门的选择子)的RPL必须在数值上<=目标TSS描述符或者任务门的DPL;
(2)异常、中断(int n指令发起的中断除外)和IRET指令引起的任务切换忽略目标任务门或者TSS描述符的DPL;
(3)对于int n指令发起的中断,要检查DPL,要求在数值上,CPL<=任务门描述符的DPL;
- 检查新任务的TSS描述符是否已经标记为有效(P=1),并且界限也有效(>=103);
- 检查新任务是否可用。对于以CALL,JMP,异常或者中断发起的任务切换,要求B=0;对于IRET发起的任务切换,要求B=1;
- 检查当前任务和新任务的TSS,以及所有在任务切换时用到的段描述符已经安排到系统内存中;
- 如果任务切换是由JMP或者IRET发起的,处理器清除当前任务的B标志;如果是由CALL指令、异常或者中断发起的,当前任务的B位保持原来的状态(=1);
- 处理器建立EFLAGS寄存器的一个临时副本。如果任务切换由IRET指令发起,则清除副本中的NT标志;如果是由CALL、JMP、异常或者中断发起的,则保持副本中NT标志不变。
- 保存当前任务的状态到它的TSS中:所有通用寄存器、段寄存器中的段选择子、刚才那个EFLAGS的副本,以及EIP;
- 加载新任务的EFLAGS寄存器。如果任务切换是由CALL、异常或者中断发起的,处理器把EFLAGS的NT标志置位;如果是由IRET或JMP发起的,NT位不变。
- 如果任务切换是由CALL、JMP、异常或者中断发起的,处理器将新任务TSS描述符中的B标志置位;如果由IRET发起,B保持原来的状态(=1);
- 用新任务的TSS选择子和TSS描述符加载TR;
- 新任务的TSS状态数据被加载到处理器:包括LDTR寄存器、CR3、EFLAGS、EIP、通用寄存器、段选择子;
- 与段选择子相对应的描述符在验证后也被加载;
- 开始执行新任务。
任务切换时,新任务的特权级别并不是从那个被挂起的任务继承来的。新任务的特权级别是由其段寄存器CS的低2位决定的,而该寄存器的内容取自新任务的TSS。因为每个任务都有自己独立的地址空间和任务状态段TSS,所以任务之间是彼此隔离的,只需要用特权级规则控制对TSS的访问就行,软件不需要在任务切换时进行显式的特权级检查。
下图是不同任务切换方法对B、NT和任务链接域的影响