《80X86汇编语言程序设计教程》十二 任务状态段控制门和控制转移
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《80X86汇编语言程序设计教程》十二 任务状态段控制门和控制转移相关的知识,希望对你有一定的参考价值。
1、 每个任务有一个任务状态段TSS,用于保存相关信息,以便在任务内变化特权级和任务切换时使用。控制任务内特权级变换的转移以及控制各个任务的切换,都需要通过控制门。
2、 系统描述符
在之前介绍存储段描述符的时候曾提过,描述符分为3种:存储段描述符、系统段描述符、门描述符(控制描述符),这里将介绍到接下来的两种。
系统段是为了实现存储管理机制所使用的一种特别的段,80386有两种系统段:任务状态段TSS和局部描述符表LDT段。用于描述系统段的描述符又叫系统描述符,也被称为特殊段描述符。
1) 系统段描述符的一般格式
m + 7 |
m + 6 |
m + 5 |
m + 4 |
m + 3 |
m + 2 |
m + 1 |
m |
Base 31…24 |
Attributes |
Segment Base 23…0 |
Segment Limit 15…0 |
其中段属性Attributes具体内容如下:
m + 6 |
m + 5 |
||||||||||||||
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
G |
X |
0 |
AVL |
Limit(19…16) |
P |
DPL |
DT |
TYPE |
|||||||
它与存储段描述符很相像,标识位是DT,当DT = 1的时候是存储段描述符,而当DT = 0的时候是系统段描述符。其中X位不用,在存储段描述符中对应于D位。除了TYPE表达的类型意义不一样以外,其它的各个域都与存储段描述符一致(参考“《80X86汇编语言程序设计教程》九 分段管理机制及纯DOS环境搭建”)。TYPE域各值对应意义如下:
类型编码 |
说明 |
类型编码 |
说明 |
0 |
未定义 |
8 |
未定义 |
1 |
可用286TSS |
9 |
可用386TSS |
2 |
LDT |
A |
未定义 |
3 |
忙的286TSS |
B |
忙的386TSS |
4 |
286调用门 |
C |
386调用门 |
5 |
任务门 |
D |
未定义 |
6 |
286中断门 |
E |
386中断门 |
7 |
286陷阱门 |
F |
386陷阱门 |
可见,只有编码为1、2、3、9、B的描述符才是真正的系统段描述符(LDT或者TSS),其它类型描述符是门描述符(可见,通过DT位,可以判断是否为存储段描述符,如果DT=0,则则根据TYPE进一步确定具体是哪种门描述符还是哪种系统段描述符)。在系统描述符中,只有2(LDT)、9(可用386TSS)、B(忙的TSS)用于386。
2) LDT段描述符
用于描述任务的局部描述符表段,LDT段描述符必须安排在GDT中才有效,在装载LDTR寄存器时,描述符中的LDT段基址和段界限等信息装入LDTR的高速缓冲寄存器中。更详细参考“《80X86汇编语言程序设计教程》八 80386程序设计基础”的“系统地址寄存器”中关于“局部描述符表寄存器LDTR”的介绍,简要说明了LDTR、如何转载它以及它对应的高速缓冲寄存器的相关内容。
LDT描述符结构类型可定义如下:
1 DESCRIPTOR struct 2 LimitL dw 0 ;段界限低16位 3 BaseL dw 0 ;基地址低16位 4 BaseM db 0 ;基地址中间8位 5 Attributes dw 0 ;段属性(含段界限高4位) 6 BaseH db 0 ;基地址高8位 7 DESCRIPTOR ends
3) TSS描述符
用于保存任务的各种状态信息。TSS描述符规定当前任务状态段的基地址与任务状态段的大小。在装载任务状态寄存器TR时,描述符中的TSS段基地址和段界限等信息被载入TR对应的高速缓冲寄存器中。在任务切换或执行LTR指令时,要装载TR寄存器。参考“《80X86汇编语言程序设计教程》八 80386程序设计基础” 的“系统地址寄存器”中关于“任务状态段寄存器TR”的介绍。
TSS中的类型规定,TSS要么为“忙”,要么为“可用”。如果一个任务是当前正执行的任务,或者是用TSS中的链接字段沿挂起任务链接到当前任务上的任务,那么该任务就是“忙”的任务,否则为“可用”任务。利用段间转移指令JMP和段间调用指令CALL,直接通过TSS描述可实现任务切换。任务状态段以及TSS描述符结构类型定义后续。
3、 门描述符
描述的不是内存段,而是控制转移的入口点。好比一个通向另一代码段的门,通过门,可以实现任务内特权级的变换和任务间的切换,因此门描述符也叫做控制门。它们存在于GDT中。
1) 门描述符的一般格式
m + 7 |
m + 6 |
m + 5 |
m + 4 |
m + 3 |
m + 2 |
m + 1 |
m |
Offset 31…16 |
Attributes |
selector |
Offset 15…0 |
其中段属性Attributes具体内容如下:
m + 5 |
m + 4 |
||||||||||||||
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
P |
DPL |
DT |
TYPE |
0 0 0 |
Dword Count |
||||||||||
其中偏移字节5与系统段描述符保持一致,selector是16位选择子,而offset是32位的偏移量。DT = 0。TYPE的值从上面的TYPE表格中取,可选值为4、5、6、7、C、E、F,其中386可取5、C、E、F。从表中可看到,门分为:调用门、任务门、中断门、陷阱门
门描述符结构类型可定义如下:
1 GATE struct 2 OffsetL dw 0 ;偏移量(0~15) 3 Selector dw 0 ;选择子 4 Dcount db 0 ;双字计数字段 5 GType db 0 ;门类型 6 OffsetH dw 0 ;偏移量(16~31) 7 GATE ends
2) 调用门
DT = 0且TYPE = 4(286调用门)或者TYPE = C(386调用门)。描述某个子程序入口(相当于一个访问接口,保存了一个局部入口地址与若干属性)。调用门内的选择子必须指向代码段描述符,调用门内的偏移是对应代码段内的偏移。利用段间调用指令CALL,可通过调用门实现从任务内外层特权级切换到内层特权级。“Dword Count”字段只在调用门中有效,传输传递时,如果需要特权等级切换,就要将外层堆栈中的参数复制到内存堆栈。该双字用于说明要复制的双字参数的数量。
3) 任务门
用于指示任务。其选择子必须指向GDT中的任务状态段TSS描述符,门中的偏移没有意义。任务的入口点保存在TSS中。利用段间转移指令JMP和段间调用指令CALL,通过任务门可实现任务切换。
4) 中断门和陷阱门
描述中断/异常处理程序的入口点。选择子必须指向代码段描述符,门内的偏移就是对应代码段的入口点偏移。中断门和陷阱门只有在中断描述符表IDT中才生效。
4、 任务状态段
TSS(Task State Segment)用于保存任务重要信息的特殊段,使用TSS描述符来描述。TR可见的16位部分含有当前任务的任务状态段描述符的选择子,TR不可见部分含有当前任务状态段的段基址和段界限等信息。
TSS在任务的切换(挂起和恢复)起重要作用,切换时,首先处理器将当前相关信息自动保存到TR所指定的TSS中(现场保护),然后下一个任务的TSS选择子装载入TS,最后根据TS指定的TSS中读取下一个任务在上次保存的信息(现场恢复)。TSS格式如下:
区域 |
31~16 |
15~0 |
偏移 |
|
链接字段 |
0000000000000000 |
链接字段 |
00H |
|
内存堆栈指针区域
|
ESP0 |
04H |
||
000000000000000 |
SS0 |
08H |
||
ESP1 |
0CH |
|||
000000000000000 |
SS1 |
10H |
||
ESP2 |
14H |
|||
000000000000000 |
SS2 |
18H |
||
地址映射寄存器区域 |
CR3 |
1CH |
||
寄存器保存区域
|
EIP |
20H |
||
EFLAGS |
24H |
|||
EAX |
28H |
|||
ECX |
2CH |
|||
EDX |
30H |
|||
EBX |
34H |
|||
ESP |
38H |
|||
EBP |
3CH |
|||
ESI |
40H |
|||
EDI |
44H |
|||
000000000000000 |
ES |
48H |
||
000000000000000 |
CS |
4CH |
||
000000000000000 |
SS |
50H |
||
000000000000000 |
DS |
54H |
||
000000000000000 |
FS |
58H |
||
000000000000000 |
GS |
5CH |
||
地址映射寄存器区域 |
000000000000000 |
LDT |
60H |
|
其它字段 |
I/O许可位图I偏移 |
00000000000000 |
T |
64H |
TSS基本格式有104个字节,分为链接字段、内存堆栈指针区域、地址映射寄存器区域、寄存器保存区域、地址映射寄存器和其它字段5个区域。
1) 寄存器保存区域
TSS正在进行时,该区域未定义,在切换任务时用来保护各个寄存器。下次切换回任务时,再从中恢复各个寄存器的值。
2) 内存堆栈指针区域
保护一个任务在不同特权等级下使用的不同堆栈。特权级0~特权级2分别对应SS0:ESP0~SS2:ESP2,最外层特权级3的堆栈信息已经在寄存器保存区域中的SS:ESP中保存。一个任务可能有4个特权级的代码,在代码特权级切换时堆栈做相应的切换。在向内层转移时,内存堆栈总是被认为是一个空栈,内层堆栈指针装载到SS:ESP,而外层原来的堆栈指针被暂存到内层堆栈中。由内层转移到外层时,从堆栈中取出外层堆栈指针并进行恢复。
3) 地址映射寄存器区域
由虚拟地址空间到线性地址空间的映射由GDT和LDT确定,GDT整个系统只有一张表,起始地址被保存在GDTR寄存器中,而LDT每个任务可以有一张表(也可以没有),选择子被保存在LDTR中,在任务切换时(或者初始化时)系统自动查表。如果没有开启分页机制(CR0中的PG位为0),那么线性地址就是物理地址;如果开启了分页机制,那么线性地址转换为物理地址通过分页管理机制实现,每个任务有自己独立的一套页表,页表分为2级,一级页表也叫目录表,它的起始物理地址被保存到CR3中(唯一一个存放物理地址的寄存器),所以线性地址空间到物理地址空间的映射由LDTR和CR3确定(具体的转换步骤参考“《天书夜读:从汇编语言到windows内核编程》十四 CPU权限级与分页机制”)。任务切换时,处理器自动从TSS中取出CR3和LDTR的值装载到相应寄存器,从而确定了每个任务各自不同的虚拟空间到物理空间的映射。需要注意的是:在切换任务前的保护现场过程中,处理器并不会自动将当前的CR3与LDTR保存到TSS的地址映射寄存器区域,所以,如果程序改动了它们的值,程序要自行负责保存它们到地址映射寄存器区域。
4) 链接字段
链接字段起链接作用,低16位保存前一个任务的TSS描述符的选择子。如果当前任务由段间调用指令CALL或者中断/异常而激活,那么链接字段保存被挂起任务TSS选择子,而且EFLAGS中的NT位(14位,嵌套任务标志)被置1,使链接字段有效。返回时,由于NT位为1,中断返回指令IRET将使得控制沿着链接字段所指恢复到链上的上一个任务。
5) 其它字段
I/O许可位图位用于实现输入/输出保护,作为TSS的扩展部分存在,关于它的详细介绍后续。T位为调试陷阱位,该字的其他位被保留,必须被置为0,在发生任务切换时,如果T = 1,那么任务切换完成后,新任务的第一条指令执行前将产生调试陷阱。
6) 用结构类型定义TSS
TSS描述符结构类型可定义如下:
1 TASKSS struct 2 TRLink dw ?,0 ;链接字 3 TRESP0 dd ? ;0级堆栈指针 4 TRSS0 dw ?,0 ; 5 TRESP1 dd ? ;1级堆栈指针 6 TRSS1 dw ?,0 ; 7 TRESP2 dd ? ;2级堆栈指针 8 TRSS2 dw ?,0 ; 9 TRCR3 dd ? ; CR3 10 TEIP dd ? ;EIP 11 TREFLAGES dw ?,? ;EFLAGS 12 TREAX dd ? ;EAX 13 TRECX dd ? ;ECX 14 TREDX dd ? ;EDX 15 TREBX dd ? ;EBX 16 TRESP dd ? ;ESP 17 TREBP dd ? ;EBP 18 TRESI dd ? ;ESI 19 TREDI dd ? ;EDI 20 TRES dw ?,0 ;ES 21 TRCS dw ?,0 ;CS 22 TRSS dw ?,0 ;SS 23 TRDS dw ?,0 ;DS 24 TRFS dw ?,0 ;FS 25 TRGS dw ?,0 ;GS 26 TRLDT dw ?,0 ;LDT 27 TRFLAG dw 0 ;TSS的特别属性字 28 TRTRAP dw 0 ;调试陷阱标志(只用位0) 29 TRIOMAP dw $+2 ;指向I/O许可位图的指针 30 TASKSS ends
5、 控制转移
可分为两类:同一任务内的控制转移和任务间的控制转移(任务切换)。同一任务内的控制转移又分为:段内转移、特权级不变的段间转移、特权级变化的段间转移。与实模式相似,JMP、CALL可分为段间直接转移(指令中直接含有目标地址指针)与段间间接转移(指令中含有指向包含目标地址指针的门描述符或者TSS描述符的指针---此时选择子有效,偏移无效),实际上,当指令中的选择子部分指示的是代码段描述符,那么就是段间直接转移(描述符中的段基址与指令中的偏移部分共同表示目标代码入口点);当选择子指示的是门描述符或TSS描述符时,就是段间间接转移(入口地址的段基址与偏移包含在对应的描述符中)。向目标代码段转移的一般步骤如下:
a)判断目标地址指针内的段选择子是否为空描述符,如果为空,且TI = 0(即前14位为0),那么指向了GDT的第0个描述符,引发保护异常。
b)从GDT或者LDT表中读出目标代码段描述符。
c)检测描述符类型是否正确(包括目标地址是不是为代码段描述符、门描述符或者TSS描述符),根据情况选择调整或者不调整RPL---请求特权等级,段选择子最低2位(比如说在调用门中就会将RPL调整为0)。
d)把目标代码段描述符内的有关内容装载到CS高速缓冲寄存器中,根据情况类似装载其它段描述符到对应高速缓冲寄存器中。进行保护检测(或者说特权检测,保护检测的情况比较复杂,各种不同的情况检测的条件不同)。
e)判断目标地址指针内偏移量是否越界(根据段界限Limit判断,具体参考“《80X86汇编语言程序设计教程》九 分段管理机制及纯DOS环境搭建”中关于段界限的介绍)
f)装载CS段寄存器(此时CPL = 段选择子的RPL)和指令指针寄存器EIP;根据情况跳转前CPL存入或者不存入CS内选择子的RPL字段(存入,则将跳转前CPL覆盖当前段选择子RPL,实现无权限等级变换的转移)。
以上的一般步骤主要有几个变数,它们在不同情况下会执行不同的操作:一个是RPL是否调整,一个是检测保护的具体机制、还有一个是CPL是否装载的问题。这些具体分支判断步骤根据具体情况各不相同,特别是保护检测部分。在开始介绍控制转移前,须明确几个概念,接下来的内容将围绕着它们:
a)CPL(Current Priviliege Level):代表了当前代码段的特权等级,存在于CS段选择子的最低2位。通常情况下是程序当前执行代码所在段的特权级。CPL = CS.RPL。
b)DPL(Descriptor Priviliege Level):表示段或者门的特权等级。它存储在段或者门描述符的DPL字段中。当当前代码段试图访问一个段或者门时,DPL将会和CPL以及RPL作比较,根据段或者门类型的不同,DPL将会被区别对待:
i)在数据段描述符中:DPL规定了访问该数据段的最外层特权级。一般要求CPL<=DPL,RPL<=DPL才能访问。
ii)在代码段描述符中:DPL规定了执行该代码段所须的CPL。要求比较复杂,分一致代码段和非一致代码段,jmp指令与call指令的区别,具体见“任务内不同特权级的变换”。
iii)在调用门和任务门描述符中:DPL规定了访问门的最外层特权级。要求同数据段描述符。
iv)在任务状态段(TSS)描述符中:DPL规定访问TSS的最外层权限级。要求同数据段描述符。
c)RPL(Requested Priviliege Level):表示请求特权级,存在于段选择子的最低2位。RPL是对于段选择子而言的,段选择子中的段描述符索引相同时,总是可以索引到同一个段,这类具有相同索引而不同RPL的段选择子,其中的RPL就代表了它们各自请求特权级,到底允不允许被调用,这个由CPL以及RPL和DPL的关系来决定。所以,它一般用于特权检查。特别是在切入内层一致代码段后的返回时,为了保证特权级不变,必须使用到RPL。
此外,在任务内代码权限等级转移时,有如下定律:
a)所有跳转,CPU不会将选择子的RPL直接赋值给跳转后程序的CPL(CS.RPL)
b)对于无特权级变换的转移,跳转后CPL(CS.RPL) = 跳转前CPL(CS.RPL)
c)对于有特权级变换的转移,跳转后CPL(CS.RPL) = 跳转后CS段描述符中的DPL
各种转移在一般步骤上的具体区别如下:
1) 任务内无特权级变换的转移
各种段内转移与实方式下相似,不涉及特权等级和任务切换。而段间无特权变换的转移是指:在转移到新的代码段时,当前的CPL保存不变(一般步骤中的第6点中CPL存入CS选择子的RPL字段,从而使CPL保持不变)。
a)段间转移指令
同实模式,JMP、CALL、RET、INT(总是)、IRET(总是)都可引起段间转移,中断/异常也将引起段间转移。32位实模式的段间转移总是使用48位全指针(16位段选择子+32位段内偏移)。
b)利用段间直接转移指令JMP或CALL
表示指令中地址指针指示的是一个代码段描述符的情况,CALL与JMP区别在于是否将返回地址压入堆栈,除了这个区别,它们都是直接开始上面的一般转移步骤。其中,除了第6步不调整CPL外,还有第3步中不调整RPL。由此可见,利用它们不能进行任务内特权等级变换的转移。
c)利用段间返回指令RET
表示从堆栈段弹出的地址指针指示的是一个代码段且目标代码段RPL = 当前CPL的情况,通常段间返回指令RET与段间调用指令CALL对应,由于调用时没有特权等级的变化,那么返回时也一定没有特权等级的变换,所以必定满足RPL = CPL(CS.RPL)。
d)利用调用门和其它途径
利用调用门的情况在本章的“任务内不同特权级的变换”一节中介绍,而其它途径在“《80x86汇编》十六 80386的中断和异常”中介绍。
e)装载代码段寄存器时的特权检测
i)对于非一致代码段,要求CPL = DPL,RPL <= DPL;对于一致代码段,要求CPL >= DPL(RPL不做检查)。
ii)代码段必须存在,即P = 1。
f)装载数据段和堆栈段寄存器特权检测
i)把选择子装入段寄存器DS、ES、FS或GS时,要进行的检测:
选择子不能为空
选择子指定的描述符类型必须是数据段描述符、可读可执行的代码段或一致可读可执行代码段
对于数据段和可读可执行代码段,要求CPL<=DPL,RPL<=DPL
对应段必须存在
ii)把选择子装入段寄存器SS时要进行的检测:
选择子不能为空
选择子指定的描述符类型必须为可读可写数据段描述符
要求CPL = RPL = DPL
对应段必须存在
g)实例演示参见“《80x86汇编》十三 任务内无特权级变换转移实例”
2) 任务内不同特权级的变换
同一任务内可存在4种特权级,实现从外层到内层变化的普通途径是:使用段间调用指令CALL,通过调用门进行转移;实现特权级从内层到外层变换的普通途径是:使用段间返回指令RET。须注意:不能使用JMP指令实现任务内不同特权级的变化。
a)通过调用门的转移
当段间转移指令JMP(不能实现特权级变化)和段间调用指令CALL所含指针的选择子指示调用门描述符时(此时指令中的偏移部分被丢弃),可实现通过调用门的转移。调用门描述符中包含了转移入口地址的48位全指针。
处理器采用和访问数据段相同的特权等级规则控制对门描述符的访问:只有在相同级或者更内层级的程序才能访问门(source.CPL<=gate.DPL),同时,还必须满足source.RPL<=gate.DPL。此外,进入门后,还须检测门内的段选择子指示的描述符必须为代码段描述符。在装载高速缓冲寄存器之前将调整source.RPL = 0,即调用门中源代码选择子的RPL被忽略(也就是说,只要通过门检查进入了门,那么就认为它具有最高请求级别,source.RPL<=destination.DPL总是满足)。
在满足以上条件后(已经进入门),在装载CS高速缓冲寄存器时(进入目标代码段前)还要对目标代码描述符进行保护检测(以下检测调用目标代码描述符中的DPL而不是调用门的DPL):
i)对于用调用门段间JMP指令:与段间直接JMP检测条件相同,对于一致代码段,须source.CPL >= destination.DPL(注意,在source.CPL>destination.DPL情况下进入目标代码以后,destination.CPL的值并不改变,也就是说发生无特权变化转移);对于非一致代码段,当source.CPL = destination.DPL时发生无特权变化转移;其它情况引发异常。
ii)对于用调用门段间CALL指令:对于一致代码段,在满足source.CPL>=destination.DPL时发生无特权变化转移;对于非一致代码段,当source.CPL = destination.DPL时发生无特权变换转移,当source.CPL > destination.DPL时,发生向内层特权级变换的转移,此时CPL将改变为destination.DPL,同时切换到对于的内存堆栈。
表格参见“《天书夜读:从汇编语言到windows内核编程》十四 CPU权限级与分页机制”
须注意:CALL指令在无特权变换时,不发生堆栈切换,返回地址保存在原堆栈中;如果特权级变换,那么返回地址保存在内层堆栈中。
b)堆栈切换
在source.CPL <= gate.DPL且source.RPL <= gate.RPL时段间CALL指令进入调用门,在门内做保护检测,如果为非一致代码段,且有source.CPL > destination.DPL,那么需要往内存切换特权等级,此时堆栈也做切换。堆栈切换的过程为:
i)根据切换后的特权等级使用TSS中相应的堆栈(SS0:ESP0~SS2:ESP2),并建立一个空栈
ii)把外层当前堆栈指针SS:ESP寄存器的值压入内层堆栈
iii)从外层堆栈复制调用参数到内层堆栈(即主程序通过堆栈给子程序的实参,复制以双字为单位)
iv)把调用者的返回地址压入堆栈。
须注意:任何情况下,如果不发生特权等级的变换,则不会进行堆栈切换。
c)向外层返回
与段间调用CALL向内层变换项对应的是使用RET实现段间返回,其步骤为:
i)从堆栈弹出返回地址
ii)如果返回地址选择子的RPL是当前CPL更外层的级,那么说明在进入时发生了权限等级的向内切换,此时需要向外层切换返回;否则,没有进行权限等级的切换,也就没有栈的切换,直接跳入第5步。
iii)在向外层返回时,跳过进入时压入的参数堆栈区域,从内存堆栈弹出外层的堆栈指针,装入SS:ESP,恢复调用前的现场,并调整内存ESP到最空栈位置。
iv)检查DS、ES、FS和GS,保证在外层可访问,如果不可访问则装入空选择子。
v)返回外层继续执行。
d)实例演示参见“《80x86汇编》十四 任务内特权级变换转移实例 ”
3) 任务切换
段间JMP、段间CALL、任务门、TSS、中断/异常或IRET都可以切换任务。
a)直接通过TSS进行任务切换
远JMP与远CALL段选择子是一个可用TSS描述符,正常情况下发生任务切换,目标任务入口点由TSS的CS和EIP字段指定而JMP或CALL指令内偏移被丢弃。TSS描述符访问特权级控制同数据段。
b)通过任务门进行任务切换
任务门内选择子指示某个任务的TSS描述符,偏移无意义,过程同上。进入任务门以后将对TSS描述符的可用性进行保护检测,通过则开始任务切换。
c)任务切换过程
i)测试TSS基本格式,TSS界限应不小于103(基本格式有104个字节)。
ii)把寄存器现场保存到当前任务TSS,不保存LDTR与CR3。注意:EIP保存的是返回地址。
iii)把目标任务TSS选择子装入TS,TSS描述符装入TR高速缓冲寄存器,当前任务变原任务,目标任务变当前任务。
iv)基本恢复当前任务寄存器现场(其中段寄存器不装载高速缓冲寄存器),恢复CR3寄存器
v)进行链接处理。嵌套任务时,将原任务TSS选择子写入当前任务TSS链接字段,当前任务TSS类型改为“忙”,EFLAGS的NT位置1。如果要解链,将原任务TSS描述符类型改为“可用”。无链接处理时,原任务TSS类型为“可用”,当前任务TSS类型为“忙”(JMP指令引起任务切换不进行链处理,CALL、中断、IRET会进行链处理)。
vi)CR0的TS位置1,表示已进行任务切换,目的是使当前使用协处理器发生自陷,完成协处理器相关现场保护和恢复。
vii)把TSS中的CS选择子RPL作为CPL(也就是说任务切换可以在任何特权等级发生,切换到任何特权等级的另一个任务)。
ix)装载LDTR寄存器。当任务没有LDT时,TSS中的LDT选择子为空,LDT的存在位置为0;否则从GDT读出LDT描述符,测试后装入LDTR高速缓冲寄存器。
x)装载各高速缓冲寄存器。
xi)把调试寄存器DR7中局部启用位设置为0,清除局部于原任务的各个断点和方式。
d)任务状态和嵌套说明
i)段间JMP指令引起任务切换(目标任务可用),不进行链接,不导致任务嵌套。原任务“可用”,目标任务“忙”。
ii)段间CALL指令以及中断/异常引起任务切换时(目标任务可用),导致任务嵌套。原任务“忙”,目标任务“忙”。
iii)IRET指令引起任务切换时(目标任务忙),实施解链,原任务“可用”,目标任务“忙”。
e)实例演示参见“《80x86汇编》十五 任务切换实例”
以上是关于《80X86汇编语言程序设计教程》十二 任务状态段控制门和控制转移的主要内容,如果未能解决你的问题,请参考以下文章
《80X86汇编语言程序设计教程》十一 32位代码段和16位代码段切换实例
《80X86汇编语言程序设计教程》二十四 进入与离开V86模式实例
《80X86汇编语言程序设计教程》二十五 结语(读后感:这本书怎么样)