汇编教程:控制转移(3)
2009-10-12 09:36:41 来源:WEB开发网3.关于实例五的说明
程序中部分片段的背景和实现方法在前面的实例中做过介绍,下面主要就任务切换和通过调用门实现任务内特权级变换时参数的复制等情形做些说明:
(1)从临时任务直接通过TSS切换到演示任务
从实模式切换到保护模式后,就认为进入了临时任务。但TR并没有指向临时任务的TSS。在从临时任务切换到演示任务时,要把临时任务的现场保存到临时任务的TSS,这就要求TR指向临时任务的TSS。所以首先要使用LTR指令把指向临时任务TSS描述符的选择子装入TR。在利用LTR指令显示地装载TR时,并不引用TSS的内容,所以临时任务的TSS几乎没有初始化。理由是这不是真正的任务切换。
临时任务采用段间转移指令JMP,直接指向演示任务的TSS,切换到演示任务。在执行切换到演示任务的段间转移指令JMP时,CPL=0,JMP指令中所含选择子内的RPL=0,演示任务TSS的描述符特权级DPL=0,并且是一个可用的TSS,所以顺利进行从临时任务到演示任务的切换。切换过程包括:把临时任务的执行现场保存到临时任务的TSS中;从演示任务的TSS中恢复演示任务的现场;把演示任务的LDT描述符选择子装载到LDTR等。从源程序可见,初始化后的演示任务的TSS中CS字段存放的选择子是DemoCode_Sel,对应的描述符在演示任务的LDT中,并且DPL=2,它描述了代码段DemoCode;挂起点是DemoBegin,所以在切换到演示任务后从该点开始执行,并且CPL=2。由于使用JMP指令进行任务切换,所以不实施任务链接。
(2)从演示任务通过任务门切换到临时任务
演示任务采用段间转移指令JMP,通过任务门ToTempT切换到临时任务。在执行切换到临时任务的段间转移指令JMP时,CPL=2,JMP指令中所含选择子ToTempT_Sel内的RPL=0,它指示的任务门的描述符特权级DPL=3,所以可以访问该任务门。任务门内的选择子TempTSS_Sel指示临时任务的TSS,并且此时的临时任务TSS是可用的,所以可顺利进行任务切换。演示任务的现场保存到演示任务的TSS;临时任务的现场从临时任务的TSS恢复。
临时任务的挂起点是临时任务代码段的ToRela点,所以恢复后的临时任务从该点开始,CS含临时任务代码段的选择子。但由于在演示任务内“强硬”地改变了临时任务TSS内的SS和DS等字段,所以在恢复到临时任务时,SS和DS等段寄存器内已含有规范数据段的选择子,而非挂起时的原有值。注意,这种做法不被提倡,但在这里却充分地展示了如何从TSS恢复任务。
(3)演示任务内的特权级变换和堆栈传递参数
演示任务采用段间调用指令CALL,通过调用门ToSubR调用子程序SubRB。执行段间调用指令CALL时的CPL=2,指令所含指向调用门的选择子的RPL=2,调用门的DPL=3,所以对调用门的访问是允许的;尽管调用门内的选择子的RPL=3,但由于它所指示的子程序代码段描述符的DPL=0,所以在调用过程中就发生了从特权级2到特权级0的变换,同时堆栈也被切换。
演示代码段通过堆栈传递了两个参数给子程序SubRB。在把参数压入堆栈时,CPL=2,使用的也是对应特权级2 的堆栈。通过调用门进入子程序后,CPL=0,使用0级堆栈。为此,把调用门ToSubR中的DCount字段设置为2,表示在特权级向内层变换时,需从外层堆栈依次复制2各个双字参数到内层堆栈。随着特权级变换,堆栈也跟着变换。这种在堆栈切换的同时复制所需参数的做法,保证了子程序方便地访问堆栈中的参数,而无需考虑是哪个堆栈。
随着从子程序SubRB的返回,CPL=0变换为CPL=2,堆栈也回到2级堆栈。由于再次进入0级堆栈,总是从空开始,所以在返回前不是非要保持内层堆栈平衡不可。但2级堆栈中的2个双字参数需要废除。从源程序可见,这是采用带立即数的段间返回指令实现的,在返回的同时,自动废除外层堆栈中的参数,同时也废除了内层堆栈中的参数。
(4)别名技术的应用
关于别名技术,前文已经作过介绍。实例五也有两处应用了别名技术。
为了把调用门ToSubR中的DCount字段设置成2,使用一个数据段描述符ToDLDT描述调用门所在演示任务的LDT段,该描述符把演示任务的LDT段描述成数据段。
还有一处是把临时任务的TSS视为普通数据段。从演示任务切换到临时任务之前,把指向描述规范数据段的描述符Normal的选择子Normal_Sel填到临时任务TSS中的各数据段寄存器(包括堆栈段寄存器)字段,于是在切换到临时任务时,作为恢复临时任务的现场,该选择子就被装到DS等数据段寄存器,对应的描述符Normal内的信息也就被装入到对应的高速缓冲寄存器中,达到为从临时任务切换到实模式作准备的目的。
更多精彩
赞助商链接