两种基本的任务切换方式
协同式:从一个任务切换到另一个任务,需要当前任务主动地请求暂时放弃执行权,或者在通过调用门请求操作系统服务时,由操作系统”趁机”将控制转移到另一个任务.这种方式依赖于每个任务的”自律”性,当一个任务失控时,其他任务可能得不到执行的机会.
抢占式:这种方式下,可以安装一个定时器中断,并在中断服务程序中实施任务切换.硬件中断信号总会定时出现,不管处理器当时在做什么,中断都会适时地发生,而任务切换也就能够顺利进行.在这种情况下,每个任务都能获得平等的执行机会.而且,即使一个任务失控,也不会导致其他任务没有机会执行.
任务切换前置
所有任务共享一个全局空间,这是内核或者操作系统的,包括了系统服务程序和数据;同时,每个任务还有自己的局部空间,每个人物的功能都不一样,所以,局部空间包含的是一个任务区别于其他任务的私有代码和数据.如下图:
在一个任务内,全局空间和局部空间具有不同的特区级.使用门,可以在任务内将控制从3特权级别的局部空间转移到0特权级的全局空间,以使用内核或者操作系统提供服务.
任务切换是以任务为单位的,是指离开一个任务,转到另一个任务中去执行.
要执行任务切换,系统中必须至少有两个任务,而且已经有一个正在执行中.
任务切换的方法
1.借助中断进行中断
- 这也是现代抢占式多任务的基础.只要中断没有被屏蔽,它就随时能发生.特别是定时器中断,能够以准确的时间间隔发生,可以用来强制实施任务切换.
在保护模式下,中断向量表不再使用,取而代之的是中断描述符表.它和GDT, LDT是一样的,用于保存描述符.唯一不同的地方是,它保存的是门描述符,包括中断门,陷阱门和任务门.当中断发生时,处理器用中断号乘以8(因为每个描述符8字节),作为索引访问中断描述符表,取出门描述符.门描述符中有中断处理过程的代码段选择子和段内偏移量,这和调用门是一样的.接着,转移到相应的位置去执行.
回忆一下调用门的工作原理,它只是从任务的局部空间转移到更高特权级的全局空间去执行,本质上是一种任务内的控制转移行为.与此相同,中断门和陷阱门允许在任务内实施中断处理,转到全局空间去执行一些系统级的管理工作,本质上,也是任务内的控制转移行为.
在中断发生时,如果该中断对应的门是任务门,那么,必须进行任务切换.任务门描述符中的主要成分是TSS选择子.任务门用于在中断发生时执行任务的切换,而执行任务切换时必须找到新任务的任务状态段.所以任务门应当指向任务的TSS.
任务门描述符中P位指示该门是否有效,当P位为0时,不允许通过此门实施任务的切换;DPL是任务门描述符的特权级,但是对因为中断而发起的任务切换不起作用,处理器不按特权级施加任何保护.
①当中断发生时,处理器用中断号乘以8作为索引访问中断描述符表.当它发现这是一个任务门(描述符)时,就知道应当发起任务切换.
②于是,它取出任务门描述符;再从任务门描述符中取出新任务的TSS选择子;接着,再用TSS选择子访问GDT,取出新任务的TSS描述符.
③在转到新任务执行前,处理器要先把当前任务的状态保存起来.当前任务的TSS是由任务寄存器TR的当前内容指向的,所以,处理器把每个寄存器的”快照”保存到由TR指向的TSS中.
④然后,处理器访问新任务的TSS,从中恢复各个寄存器的内容.
⑤最终,任务寄存器TR指向新任务的TSS,而处理器随即开始执行新的任务.一旦新任务开始执行,处理器固件会自动将其TSS描述符的B位置1,表示该任务的状态为忙.
中断发生时,可以执行常规的中断处理过程,也可以进行任务切换.尽管性质不同,但它们都要使用iret指令返回.前者是返回到同一任务内的不同代码段;后者是返回到被中断的那个任务.
32位处理器的SFLAGS有NT位(位14),意思是嵌套任务标志.每个任务的TSS中都有一个任务链接域(指向前一个任务的指针),可以填写为前一个任务的TSS描述符选择子.如果当前任务EFLAGS寄存器的NT位是1,则表示当前正在执行的任务嵌套与其他任务内,并且能够通过TSS任务链接域的指针返回到前一个任务.
对新任务的处理是,要把老任务的TSS选择子填写到新任务TSS中的任务链接域,同时,将新任务EFLAGS的NT位置”1”,以允许返回(转换)到前一个任务(老任务)继续执行,同时,还要把新任务TSS描述符的B位置”1”.
可以使用iret指令从当前任务返回到前一个任务,前提是EFLAGS的NT位为1.无论任务处理器碰到iret指令,它都要检查NT位, 如果为0,表示是一般的中断过程,按一般的中断返回处理;如果此位为1,则表明当前任务之所以能够正在执行,是因为中断了别的任务.因此,应当返回原先被中断的任务继续执行.此时,由处理器固件把当前任务EFLAGS的NT位置0,并把TSS描述符的B位置0(非忙).在保存了当前任务的状态之后,接着,用新任务(被中断的任务)的TSS恢复现场.
2.用CALL或者JMP直接发起任务切换
当处理器执行这两条指令时,首先用指令中给出的描述符选择子访问GDT,分析他的描述符类型,如果是一般的代码段描述符,就按普通的段间转移规则执行;如果是调用门,按调用门的规则执行;如果是TSS描述符,或者任务门,则执行任务切换.此时指令中给出的32位偏移被忽略,原因是执行任务切换时,所有处理器的状态都可以从TSS中获得.注意,任务门描述符可以安装在中断描述符表中,也可以安装在GDT或者LDT中.
如果用于发起任务切换,call指令和jmp指令也有不同之处.使用call发起的任务切换类似于因中断发起的任务切换.这就是,由call发起的任务切换是嵌套的,当前任务(旧任务)TSS描述符的B位保持原来的1不变,EFLAGS寄存器的NT位也不发生变化;新任务TSS描述符的B位置1,EFLAGS的NT位也置1,表示此任务嵌套与其他任务中.同时,TSS任务链接域的内容改为旧任务的TSS描述符选择子.
用CALL指令发起的任务切换,可以通过IRET返回前一个任务.此时,旧任务(当前任务)TSS描述符的B位,以及EFALGS的NT位都被恢复到0,并保存到它的TSS中.
用JMP指令发起的任务切换,不会形成任务之间的嵌套关系.执行任务切换时,当前任务(旧任务)TSS的B位清0,EFALGS的NT位不变;新任务TSS描述符的B位置1,进入忙的状态,EFALGS的NT位保持从TSS中加载时的状态不变.
任务是不可重入的
任务不可重入的本质是,执行任务切换是,新任务的状态不能为忙.这里有两个典型的情形:
执行任务切换时,新任务不能是当前任务自己.试想一下,如果允许这种情况发生,处理器该如何执行现场保护和恢复操作?
如图不允许使用CALL指令从任务3切换到任务2和任务1上.如果不禁止这种情况的话,任务之间的嵌套关系将会因为TSS任务链接域的破坏而错乱.
处理器是通过TSS描述符的B位来检测重入的. 因中断, IRET, CALL和JMP指令发起任务切换时, 处理器固件会检测新任务TSS描述符的B位, 如果为1, 则不允许执行这样的切换.
处理器在实施任务切换时的操作
处理器用一下4种方法将控制转移到其他任务:
当前程序,任务或着过程执行一个将控制转移到GDT内某个TSS描述符的JMP或者CALL指令.
当前程序,任务或者过程执行一个将控制转移到GDT或者当前LDT内某个任务门描述符的JMP或者CALL指令.
一个异常或者中断发生时,中断号指向中断描述符表内的任务门.
在EFLAGS寄存器的NT位置位的情况下,当前任务执行了一个IRET指令.
在任务切换是, 处理器执行以下操作:
从JMP或者CALL指令的操作数,任务门或者当前任务的TSS任务链接域取的新任务的TSS描述符选择子.最后一种方法适用于以IRET发起的任务切换.(简言之之就是准备命令切换)
检查是否允许从当前任务(旧任务)切换到新任务.数据访问的特权级检查规则适用于JMP和CALL指令,当前任务的CPL和新任务段选择子的RPL必须在数值上小于或者等于目标TSS或者任务门的DPL.异常,中断(除了int n指令引发的中断)和IRET指令引起的任务切换忽略目标任务门或者TSS描述符的DPL.对于以int n指令产生的中断,要检查DPL.(简言之就是检查特权级)
检查新任务的TSS描述符是否已经标记为有效(P = 1),并且界限值也有效(大于或者等于0x67,十进制103).(简言之就是检查TSS是否可用)
检查新任务是否可用,不忙(B = 0,对于以CALL,JMP,异常或者中断发起的任务切换),忙(B = 1,对于以iret发起的任务切换).(简言之就是新任务是否可用)
检查当前任务(旧任务)和新任务的TSS,以及所有任务切换时用到的段描述符已经安排到系统内存中.
如果任务切换是由JMP或者IRET发起的,处理器清除当前(旧)任务的忙(B)标志;如果是由CALL指令,异常或者中断发起的,忙标志保持原来的置为状态.
如果任务切换是由IRET指令发起的,处理器建立EFLAGS寄存器的一个临时副本并清除其NT标志;如果是由CALL,JMP,异常或者中断发起的,副本中的NT标志不变.
保存当前(旧)任务的状态到它的TSS中.处理器从任务寄存器中找到当前TSS的基地址,然后将以下寄存器的状态复制到它的TSS中:所有通用寄存器,段寄存器中的段选择子,刚才那个EFLAGS寄存器的副本,以及指令指针寄存器EIP.(简言之就是保存现任务状态)
如果任务切换是由CALL指令,异常或者中断发起的,处理器把从新任务加载的EFLAGS的NT标志置位;如果是由IRET或者JMP指令发起的,NT标志位的状态对应着从新任务加载的EFLAGS的NT位.
如果任务切换是由CALL,JMP,异常或者中断发起的,处理器将新任务TSS描述符中的B位置位;如果是由IRET指令发起的,B位保持原先的置位状态不变.
用新任务的TSS选择子和TSS描述符加载任务寄存器TR.
新任务的TSS状态数据被加载到处理器.这包括LDTR,PBDR(控制寄存器CR3),EFLAGS,EIP,通过用寄存器,以及段选择子.载入状态期间只要发生一个故障,构架状态就会被破坏(因为有些寄存器的内容已被改变,而且无法撤销和回退).所谓架构,是指处理器对外公开的那一部分的规格和构造;所谓架构状态,是指处理器内部的各种构件,在不同的条件下,所建立起来的确定状态.当处理器处于某种状态时,再施加另一种确定的条件,可以进入另一种确定的状态,这应当是严格的,众所周知的,可预见的.否则,就意味着架构状态遭到破坏.(TSS加载处理器时需要一定的架构不被破坏)
与段选择子相对应的描述符在经过验证后也被加载.与加载和验证新任务环境有关的任何错误都将破坏架构状态.注意,如果所有的检查和保护工作都已经成功实施,处理器提交任务切换. 如果在第11步的过程中发生了不可恢复性的错误,处理器不能完成任务切换,并确保处理器返回到执行发起任务切换的那条指令前的状态.如果在第12步发生了不可恢复的错误,架构状态被破坏; 如果在提交点(第13步)之后发生了不可恢复性的错误,处理器完成任务切换并在开始执行新任务之前产生一个相应的异常.(段选择子相应描述符的验证)
开始执行新任务.
任务切换时,当前任务的状态总要保存起来,在恢复执行时,处理器从EIP寄存器的保存值所指向的那条指令开始执行,这个寄存器的值是在当初任务切换被挂起时保存的.
任务切换时, 新任务的特权级并不是从那个被挂起的任务继承来的.新任务的特权级别是由其段寄存器CS的低2位决定的.
任务状态段TSS的任务链接域和EFLAGS的NT位用于返回前一个任务执行,当前EFLAGS寄存器的NT位是1表明当前任务嵌套与其他任务中.无论如何,新任务的TSS描述符的B位都会被置位,旧任务的B位取决于任务切换的方法.
不同任务切换方法对B位,NT位和任务链接域的影响
标志或TSS任务链接域 | JMP指令的影响 | CALL指令或中断的影响 | IRET指令的影响 |
---|---|---|---|
新任务的B位 | 置位.原先必需为零 | 置位.原先必需为零 | 不变.原先必须被置位 |
旧任务的B位 | 清零 | 不变.原先必须被置位 | 清零 |
新任务的NT标志 | 设置为新任务TSS中的对应值 | 置位 | 设置为新任务TSS中的对应值 |
旧任务的NT标志 | 不变 | 不变 | 清零 |
新任务的任务链接域 | 不变 | 用旧任务的TSS描述符选择子加载 | 不变 |
旧任务的任务链接域 | 不变 | 不变 | 不变 |