当前位置:网站首页>任务切换的细节

任务切换的细节

2022-07-23 11:10:00 raindayinrain

        在一个多任务环境中,可同时存在多个任务。每个任务有各自的局部描述符表(LDT)和任务状态段(TSS)。在局部描述符表中存放着专属于任务局部空间的段的描述符。可在多个任务之间切换,使它们轮流执行,从一个任务切换到另一个任务时,具体的切换过程是由处理器固件负责进行的。

        所谓多任务系统,指能同时执行两个以上任务的系统。任务调度主要是操作系统的责任,处理器只负责具体的切换过程,包括保护前一个任务的现场。

        有两种基本的任务切换方式,一种是协同式的,从一个任务切换到另一个任务,需当前任务主动请求暂时放弃执行权,或在通过调用门请求操作系统服务时,由操作系统"趁机"将控制转移到另一个任务。这种方式依赖任务"自律"。

        另一种是抢占式,这种方式下,可安装一个定时器中断,并在中断服务程序中实施任务切换。硬件中断信号总会定时出现,不管处理器当时在做什么,中断都会适时发生,而任务切换也就能顺利进行。这种情况下,每个任务都能获得平等的执行机会。且,即使一个任务失控,也不会导致其他任务没有机会执行。

15.1.本章代码清单

15.2.任务切换前的设置

        所有任务共享一个全局空间,这是内核或操作系统提供的,包含了系统服务程序和数据;同时,每个任务还有自己的局部空间,每个任务的功能都不一样,所以,局部空间包含的是一个任务区别于其他任务的私有代码和数据。

        在一个任务内,全局空间和局部空间具有不同的特权级别。使用门,可在任务内将控制从3特权级的局部空间转移到0特权级的全局空间,以使用内核或操作系统提供的服务。

        任务切换是以任务为单位的,是指离开一个任务,转到另一个任务中去执行。任务转移相对来说要复杂的多,当一个任务正在执行时,处理器的各个部分都和该任务息息相关:段寄存器指向该任务所使用的内存段;通用寄存器保存着该任务的中间结果,等等。

        离开当前任务,转到另一个任务开始执行时,要保存旧任务的各种状态,并恢复新任务的运行环境。

        即要执行任务切换,系统中需至少有两个任务,且已经有一个正在执行中。一开始,处理器是在任务的全局空间执行的,当前特权级别是0,然后,我们通过一个虚假的调用门返回,使处理器回到任务的局部空间执行,当前特权级别降为3。

        事实上这样没必要。首先,处理器刚进入保护模式时,以0特权级运行的,且执行的一般是操作系统代码,也需是0特权级别的。其次,任务不一定非得是3特权级别的,也可是0特权级别的。特别是,操作系统除了为每一个任务提供服务外,也会有一个作为任务而独立存在的部分,且是0特权级别的任务,以完成一些管理和控制功能,比如提供一个界面和用户进行交互。

        计算机加电后,一旦进入保护模式,就直接创建和执行操作系统的0特权级任务。然后,可从该任务切换到其他任务。

        既然如此,这一章,首先要创建0特权级别的操作系统(内核)任务。

        内核任务,一般作用是创建其他任务,管理它们,所以称作任务管理器,或叫程序管理器。

        任务状态段(TSS)是一个任务存在的标志,没有它,就无法执行任务切换,因为任务切换时需要保存旧任务的各种状态数据。

        程序管理器任务没有自己的LDT,任务可以没有自己的LDT,这是允许的。程序管理器可以将自己所使用的段描述符安装在GDT中。另外,程序管理器任务是运行在0特权级别上的,不需要创建额外的栈。因为除了从门返回外,不能将控制从高特权级的代码段转移到低特权级的代码段。

        在GDT中创建TSS的描述符,必须创建TSS的描述符,且只能安装在GDT中。

        为表明当前正在任务中执行,要做的最后一个工作是将当前任务的TSS选择子传送到任务寄存器TR中。

15.3.任务切换的方法

        对多任务的支持是现代处理器的标志之一。为此,Intel处理器提供了多种方法,以灵活地在各个任务之间实施切换。

        处理器并没有提供额外的指令用于任务切换。事实上,用的都是我们熟悉的老指令和老手段,但扩展了它们的功能,使之除了能继续执行原有的功能外,也能用于实施任务切换操作。

        第一种任务切换的方法是借助于中断,这也是现代抢占式多任务的基础。

        我们知道,实模式下,内存最低地址端的1KB是中断向量表,保存着256个中断处理过程的段地址和偏移地址。当中断发生时,处理器把中断号乘以4,作为表内索引号访问中断向量表,从相应位置取出中断处理过程的段地址和偏移地址,并转移到那里执行。

        在保护模式下,中断向量表不再使用,取而代之的,是中断描述符表。

        它类似GDT,LDT,用于保存描述符。唯一不同的地方是,它保存的是门描述符,包括中断门,陷阱门,任务门。

        当中断发生时,处理器用中断号乘以8(因为每个描述符占8字节),作为索引访问中断描述符表,取出门描述符。门描述符中有中断处理过程的代码段选择子和段内偏移量,这和调用门是一样的。接着,转移到相应的位置去执行。

        一般的中断处理可以使用中断门和陷阱门。中断门和陷阱门允许在任务内实施中断处理,转到全局空间去执行一些系统级的管理工作,本质上,也是任务内的控制转移行为。

        但是,中断发生时,如该中断号对应的门是任务门,则,性质就截然不同了,必须进行任务切换。即,要中断当前任务的执行,保护当前任务的现场,并转换到另一个任务去执行。

        任务门描述符格式

 不使用(占据16位) P DPL 00101 不使用(占据8位) TSS选择子(占据16位) 不使用(占据16位)

        任务门描述符中的P位指示该门是否有效,当P位为"0"时,不允许通过此门实施任务切换;DPL是任务门描述符的特权级,但对因中断而发起的任务切换不起作用,处理器不按特权级施加任何保护。但这并不意味着DPL字段没用处,当以非中断的方式通过任务门实施任务切换时,它就有用了。

        当中断发生时,处理器用中断号乘以8作为索引访问中断描述符表。当它发现这是一个任务门(描述符)时,就知道应当发起任务切换。于是,它取出任务门描述符;再从任务门描述符中取出新任务的TSS选择子;接着,再用TSS选择子访问GDT,取出新任务的TSS描述符。

        在转到新任务执行前,处理器要先把当前任务的状态保存起来。当前任务的TSS是由任务寄存器TR的当前内容指向的,所以,处理器要把每个寄存器的"快照"保存到由TR指向的TSS中。然后,处理器访问新任务的TSS,从中恢复各个寄存器的内容,包括通用寄存器,标志寄存器EFLAGS,段寄存器,指令指针寄存器EIP,栈指针寄存器ESP,及局部描述符表寄存器(LDTR)等。

        最终,任务寄存器TR指向新任务的TSS,而处理器旋即开始执行新的任务。一旦新任务开始执行,处理器固件会自动将其TSS描述符的B位置"1",表示该任务的状态为忙。

        当中断发生时,可执行常规的中断处理过程,也可进行任务切换。尽管性质不同,但都用iret指令返回。前者是返回到同一任务内的不同代码段;后者是返回到被中断的那个任务。

        32位处理器的EFLAGS有NT位(位14),意思是嵌套任务标志。每个任务的TSS中都有一个任务链接域(指向前一个任务的指针),可以填写为前一个任务的TSS描述符选择子。如当前任务EFLAGS寄存器的NT位是"1",则表示当前正在执行的任务嵌套于其他任务内,且能通过TSS任务链接域的指针返回到前一个任务。

保留设置为0(占据10位) ID 空 空 空 空 空 0 NT IOPL OF DF IF TF SF ZF 0 AF 0 PF 1 CF 

        因中断而引发任务切换时,取决于当前任务(旧任务)是否嵌套于其他任务内,其EFLAGS寄存器的NT位可能是"0",也可能是"1"。处理器不会改变它,而是和其他寄存器一道,写入TSS中保护起来。另外,当前任务(旧任务)肯定处于"忙"状态,其TSS描述符的B位一定是"1",在任务切换后同样保持不变。

        对新任务的处理是,要把老任务的TSS选择子填写到新任务TSS中的任务链接域,同时,将新任务EFLAGS寄存器的NT位置"1",以允许返回到前一任务继续执行。同时,还要把新任务TSS描述符的B位置"1"。

        可使用iret指令从当前任务返回到前一个任务,前提是当前任务EFLAGS寄存器的NT位必须是"1"。无论任何时候处理器碰到iret指令,都要检查NT位,如此位是0,表明是一般的中断过程,按一般的中断返回处理,即,中断返回是任务内的(中断处理过程虽然属于操作系统,但属于任务的全局空间);如此位是1,则表明当前任务之所以能执行,是因为中断了别的任务。因此,应返回原先被中断的任务继续执行。此时,由处理器固件把当前任务EFLAGS寄存器的NT位改为"0",并把TSS描述符的B位改为"0"。在保存了当前任务的状态之后,接着,用新任务(被中断的任务)的TSS恢复现场。 

        除了因中断引发的任务切换外,还可用远过程调用CALL,或远跳转指令JMP直接发起任务切换。此时,CALL和JMP指令的操作数是任务的TSS描述符选择子或任务门。

call 0x0010:0x00000000

jmp 0x0010:0x00000000 

        当处理器执行这两条指令时,首先用指令中给出的描述符选择子访问GDT,分析它的描述符类型。如是一般的代码段描述符,就按普通的段间转移规则执行;如是调用门,按调用门的规则执行;如是TSS描述符,或任务门,则执行任务切换。

        此时,指令中32位偏移量被忽略,因为执行任务切换时,所有处理器的状态都可从TSS中获得。注意,任务门描述符可安装在中断描述符表中,也可安装在全局描述符表或局部描述符表中。

        如是用于发起任务切换,call和jmp也有不同之处。使用call指令发起的任务切换类似于因中断发起的任务切换。即,由call指令发起的任务切换是嵌套的,当前任务(旧任务)TSS描述符的B位保持原来的"1"不变 ,EFLAGS寄存器的NT位也不发生变化;新任务TSS描述符的B位置"1",EFLAGS寄存器的NT位也置"1",表示此任务嵌套于其他任务中。同时,TSS任务链接域的内容改为旧任务的TSS描述符选择子。

        假设任务1是整个系统中的第一个任务。当任务1开始执行时,其TSS描述符的B位是"1",EFLAGS寄存器的NT位是"0",不嵌套于其他任务。

        当从任务1切换到任务2后,任务1仍然为"忙",EFLAGS寄存器的NT位不变(在其TSS中);任务2也变为"忙",EFLAGS寄存器的NT位变为"1",表示嵌套于任务1中。同时,任务1的TSS描述符选择子也被复制到任务2的TSS中(任务链接域)。

        最后是从任务2转到任务3执行。任务2保持"忙", EFLAGS寄存器的NT不变(在其TSS中);任务3成为当前任务,其TSS描述符的B位变为"1",EFLAGS寄存器的NT位也变成"1",同时,其TSS任务链接域指向任务2。

        用CALL指令发起的任务切换,可通过iret指令返回到前一个任务。此时,旧任务TSS描述符的B位,及EFLAGS寄存器的NT位都恢复到"0"。

        和call不同,使用jmp指令发起的任务切换,不会形成任务间的嵌套关系。执行任务切换时,当前任务(旧任务)TSS描述符的B位清零,变为非忙状态,EFLAGS寄存器的NT位不变;新任务TSS描述符的B位置"1",进入忙的状态,EFLAGS寄存器的NT位保持从TSS中加载时的状态不变。

        任务是不可重入的。

        任务不可重入的本质是,执行任务切换时,新任务的状态不能为忙。

        第一种情形,执行任务切换时,新任务不能是当前任务自己。因为如允许,处理器不好执行现场的保护和恢复。

        第二种情形,不允许使用CALL指令从任务3切换到任务2和任务1上。如不禁止这样,任务之间的嵌套关系将会因为TSS任务链接域的破坏而错乱。

        处理器是通过TSS描述符的B位来检测重入的。因中断,iret,call和jmp指令发起任务切换时,处理器固件会检测新任务TSS描述符的B位,如为"1",则不允许执行这样的切换。

---next

原网站

版权声明
本文为[raindayinrain]所创,转载请带上原文链接,感谢
https://blog.csdn.net/x13262608581/article/details/125941627