5. 处理器在实施任务切换时的操作(任务切换总结)

处理器可以通过以下四种方法实施任务切换

  1. call指令或者jmp指令的操作数是GDT内的某个TSS描述符
  2. call指令或者jmp指令的操作数是GDT或者LDT内某个任务门描述符
  3. 一个异常或者中断发生时,中断号指向IDT内的某个任务门;
  4. 在EFLAGS寄存器的NT位置位的情况下,当前任务执行了一个iret指令。

对于这四种方法,首先都可以得到一个TSS描述符的选择子:

  • 对于1,指令的操作数直接就是TSS选择子;
  • 对于2和3,任务门里包含了TSS选择子;
  • 对于4,当前任务的TSS的任务链接域内就是TSS选择子。

这个TSS选择子就决定了要切换到哪个任务。

在任务切换时,处理器执行以下操作:

  1. 取得新任务的TSS描述符的选择子(如上文所述)。
  2. 检查是否允许从当前任务切换到新任务。

(1)数据访问的特权级检查规则适用于JMP和CALL指令,当前(旧)任务的CPL和新任务段选择子(TSS描述符的选择子或者任务门的选择子)的RPL必须在数值上<=目标TSS描述符或者任务门的DPL;

(2)异常、中断(int n指令发起的中断除外)和IRET指令引起的任务切换忽略目标任务门或者TSS描述符的DPL;

(3)对于int n指令发起的中断,要检查DPL,要求在数值上,CPL<=任务门描述符的DPL;

  1. 检查新任务的TSS描述符是否已经标记为有效(P=1),并且界限也有效(>=103);
  2. 检查新任务是否可用。对于以CALL,JMP,异常或者中断发起的任务切换,要求B=0;对于IRET发起的任务切换,要求B=1;
  3. 检查当前任务和新任务的TSS,以及所有在任务切换时用到的段描述符已经安排到系统内存中;
  4. 如果任务切换是由JMP或者IRET发起的,处理器清除当前任务的B标志;如果是由CALL指令、异常或者中断发起的,当前任务的B位保持原来的状态(=1);
  5. 处理器建立EFLAGS寄存器的一个临时副本。如果任务切换由IRET指令发起,则清除副本中的NT标志;如果是由CALL、JMP、异常或者中断发起的,则保持副本中NT标志不变。
  6. 保存当前任务的状态到它的TSS中:所有通用寄存器、段寄存器中的段选择子、刚才那个EFLAGS的副本,以及EIP;
  7. 加载新任务的EFLAGS寄存器。如果任务切换是由CALL、异常或者中断发起的,处理器把EFLAGS的NT标志置位;如果是由IRET或JMP发起的,NT位不变。
  8. 如果任务切换是由CALL、JMP、异常或者中断发起的,处理器将新任务TSS描述符中的B标志置位;如果由IRET发起,B保持原来的状态(=1);
  9. 用新任务的TSS选择子和TSS描述符加载TR;
  10. 新任务的TSS状态数据被加载到处理器:包括LDTR寄存器、CR3、EFLAGS、EIP、通用寄存器、段选择子;
  11. 与段选择子相对应的描述符在验证后也被加载;
  12. 开始执行新任务。

任务切换时,新任务的特权级别并不是从那个被挂起的任务继承来的。新任务的特权级别是由其段寄存器CS的低2位决定的,而该寄存器的内容取自新任务的TSS。因为每个任务都有自己独立的地址空间和任务状态段TSS,所以任务之间是彼此隔离的,只需要用特权级规则控制对TSS的访问就行,软件不需要在任务切换时进行显式的特权级检查。

下图是不同任务切换方法对B、NT和任务链接域的影响

5. 处理器在实施任务切换时的操作(任务切换总结)