《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(五)
2015-04-15 15:12
681 查看
一、任务切换
利用段间转移指令 JMP 或者段间调用指令 CALL,通过任务门或直接通过任务状态段,可以切换到别的任务。此外,在中断/异常或者执行 IRET 指令时也可能发生任务切换。需要注意的是,因为 RET 指令的目标地址只能使用代码段描述符,所以,不能通过 RET指令实现任务切换。
1.直接通过TSS进行任务切换
段间转移指令 JMP 或段间调用指令 CALL 所含指针的选择子指示一个可用任务状态段 TSS 描述符时,正常情况下就发生从当前任务到由该可用 TSS 对应任务(目标任务)的切换。目标任务的入口点由目标任务 TSS 内的 CS 和 EIP 字段所规定的指针确定。这样的 JMP或 CALL 指令内的偏移被丢弃。另外,对于段间调用指令 CALL,若目标选择子指示一 TSS 段描述符或任务门时,则返回地址和外层栈指针并不压入堆栈。
处理器采用与访问数据段相同的特权级规则控制对 TSS 段描述符的访问。TSS 段描述符的 DPL 规定了访问该描述符的最外层特权级,只有在相同级别或更内层级别的程序才可以访问它。同时,还要求指示它的选择子的 RPL 必须满足 RPL<=TSS 的 DPL 的条件。当这些条件满足时,就开始任务切换。
2.通过任务门进行任务切换
任务门内的选择子指示某个任务的TSS描述符。当段间转移指令JMP或段间调用指令CALL所含指针的选择子指示一个任务门时,正常情况下就发生任务切换,即从当前任务切换到由任务门内的选择子所指示的 TSS 描述符对应的任务(目标任务)。这样的 JMP 或 CALL指令内的偏移被丢弃;任务门内的偏移也无意义。
处理器采用与访问数据段相同的特权级规则控制对任务门的访问。任务门的 DPL 规定了访问该任务门的最外层特权级,只有在同级或更内层级别的程序才可以访问它。同时,还要求指示任务门的选择子 RPL 必须满足 RPL<=任务门的 DPL 的条件。在这些条件满足时,再检查任务门内的选择子,要求该选择子指示 GDT 中的可用的 TSS 描述符。对于任务门所指向的 TSS 描述符的 DPL 不进行特权级检查。检查通过以后,就开始任务切换。
3.任务切换过程
根据指示目标任务 TSS 描述符的选择子进行任务切换的一般过程如下:
第一,测试目标任务状态段的界限。TSS 用于保存任务的各种状态信息,不同的任务,TSS 中可以有数量不等的其他信息,根据任务状态段的基本格式,TSS 的界限应大于或等于 103(104-1)。
第二,把寄存器现场保存到当前任务的 TSS。把通用寄存器、段寄存器、EIP 及 EFLAGS 的当前值保存到当前 TSS 中。保存的 EIP
值是返回地址,指向引起任务切换指令的下一条指令。但不把 LDTR 和 CR3 的内容保存到 TSS 中。
第三,把指示目标任务 TSS 的选择子装入 TR 寄存器中。同时把对应 TSS 的描述符装入 TR 的高速缓冲寄存器中。此后,当前任务
改称为原任务,目标任务改称为当前任务。
第四,基本恢复当前任务(目标任务)的寄存器现场。根据保存在 TSS 中的内容,恢复各通用寄存器、段寄存器、EFLAGS 及 EIP。
在装入寄存器的过程中,为了能正确地处理可能发生的异常,只把对应选择子装入各段寄存器。此时选择子的 P 位为 0。还装载 CR3 寄存器。
第五,进行链接处理。如果需要链接,那么将指向原任务 TSS 的选择子写入当前任务 TSS 的链接字段,把当前任务 TSS 描述符类型改为“忙”(并不修改原任务状态段描述符的“忙”位),并将标志寄存器 EFLAGS 中的 NT 位置 1,表示是嵌套任务。如果需要解链,那么把原任务TSS描述符类型改为“可用”。如果无解链处理,那么将原任务TSS描述符类型置为“可用”,当前任务TSS描述符类型置为“忙”。由于
JMP 指令引起的任务切换不实施链接/解链处理;由 CALL 指令、中断、IRET 指令引起的任务切换要实施链接/解链处理。
第六,把 CR0 中的 TS 标志置为 1,这表示已发生过任务切换,在当前任务使用协处理器指令时,产生自陷。由自陷处理程序完成有关协处理器现场的保存和恢复。这有利于快速地进行任务切换。
第七,把 TSS 中的 CS 选择子的 RPL 作为当前任务特权级设置为 CPL。又因为装入 CS 高速缓冲寄存器时要检测 CPL=代码段描述符的 DPL,所以 TSS 中的选择子所指示的代码段描述符的 DPL 必须等于该选择子的 RPL。任务切换可以在一个任务的任何特权级发生,并且可以切换到另一任务的任何特权级。
第八,装载 LDTR 寄存器。一个任务可以有自己的 LDT,也可以没有。当任务没有 LDT 时,TSS 中 LDT 选择子为 0。如果 TSS 中LDT 选择子非空,则从 GDT 中读出对应的 LDT 描述符,在经过测试后,把所读的 LDT 描述符装入 LDTR 高速缓冲寄存器。如果 LDT选择子为空,则将 LDT 的存在位置为 0,表明任务不使用 LDT。
第九,装载代码段寄存器 CS、堆栈段寄存器 SS 和各数据段寄存器及其高速缓冲寄存器。在装入代码段高速缓存之前,也要进行特权检查,处理器调整 TSS 中的 CS 选择子的 RPL=0,装入之后,调整 CS 的 RPL 等于目标代码段的 DPL。堆栈段使用的是 TSS 中的 SS和 SP 字段的值,而不是使用内层栈保存区中的指针,即使发生了向内层特权级的变换。这与任务内的通过调用门的转移不同。
第十,把调试寄存器 DR7 中的局部启用位置为 0,以清除局部于原任务的各个断点和方式。
4.关于任务状态和嵌套的说明
需要注意的是任务切换不能递归
在段间转移指令 JMP 引起任务切换时,不实施链接,不导致任务的嵌套。它要求目标任务是可用任务。切换过程中把原任务置为“可用”,目标任务置为“忙”。
在段间调用指令 CALL 引起任务切换时,实施链接,导致任务的嵌套。它要求目标任务是可用的任务。在切换过程中把目标任务置为“忙”,原任务仍保持“忙”;标志寄存器 EFLAGS 中的 NT 位被置为 1,表示任务是嵌套任务。
在由中断异常引起任务切换时,实施链接,导致任务的嵌套。要求目标任务是可用的任务。在切换过程中把目标任务置为“忙”,原任务仍保持“忙”;标志寄存器 EFLAGS 中的 NT 位被置为 1,表示任务是嵌套任务。
在执行 IRET 指令时引起任务切换,那么实施解链。要求目标任务是忙的任务。在切换过程中把原任务置为“可用”,目标任务仍保持“忙”。关于中断/异常如何引起任务切换和指令 IRET 如何考虑任务切换的内容将在后面的文章中论述。
二、演示任务切换的实例
下面给出一个用于演示任务切换的实例。该实例的逻辑功能是在切换后显示原任务的挂起点(EIP)的值。该实例演示内容包括:直接通过 TSS 段的任务切换,通过任务门的任务切换,任务内特权级的变换及参数传递。
1.实现步骤
为了达到任务切换和特权级变换的目的,该实例在保护模式下涉及到两个任务,一个任务为临时任务,另一个任务称为演示任务。演示任务的功能是演示通过调用门实现特权级的变换和堆栈间参数的自动复制。临时任务和演示任务配合展示任务切换实例实现步骤如下:
(1)实模式下初始化;
(2)切换到保护模式;
(3)设置 TR 对应临时任务,特权级为 0;
(4)直接切换到演示任务,演示任务的特权级为 2;(5)把入口参数压入堆栈,经调用门进入显示信息子程序,显示信息子程序的特权级为 0;
(6)从堆栈中取出入口参数并处理;
(7)从显示信息子程序返回特权级为 2 的演示代码段;
(8)为切换回实模式作部分准备工作;
(9)经任务门切换到特权级为 0 的临时任务;
(10)准备返回实模式;
(11)切换到实模式;
(12)实模式下的恢复工作。
在任务切换时,把原任务的现场保存到 TR 所指示的 TSS 内,然后再把指向目标任务的 TSS 描述符的选择子装入 TR,所以在从临
时任务切换到演示任务之前,要把指向临时任务 TSS 描述符的选择子装入 TR。通过把演示任务的 TSS 初始化成恢复点在特权级 2 的代码段,使得从临时任务切换到演示任务后,当前特权级 CPL=2。
2.源程序组织和清单
实例五由如下部分组成:
(1)全局描述符表 GDT。GDT 含有演示任务的 TSS 描述符和 LDT 段描述符,还含有临时任务 TSS 描述符和临时任务的代码段描述符,此外,还含有子程序代码段描述符、规范数据段描述符和视频缓冲区段描述符。
(2)演示任务的 TSS。以根据演示要求初始化。
(3)演示任务的 LDT 段。它含有演示任务的 0 级和 2 级堆栈段描述符、代码段和数据段描述符、分别以数据段方式描述 LDT 和临时任务 TSS 的数据段描述符、以及指向子程序的调用门和指向临时任务的任务门。
(4)演示任务的 0 级和 2 级堆栈段。32 位段,特权级分别为 0 和 2。(5)演示任务数据段。32 位段,特权级 3。
(6)子程序代码段。32 位代码段,特权级 0。(7)演示任务代码段。32 位代码段,特权级 2。
(8)临时任务的 TSS 段。未初始化。
(9)临时任务代码段。16 位代码段,特权级 0。
(10)实模式下的数据和代码段。该实例的逻辑功能是在切换后显示原任务的挂起点(EIP)的值。
源程序清单如下:
程序中部分片段的背景和实现方式在前面的实例中做过介绍,下面只要就任务切换和通过门调用实现任务内特权级变换时参数的复制等情形做些说明:
(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)别名技术的应用
关于别名技术前面已经讲过。该实例也有两处应用了别名技术。
还有一处是把临时任务的 TSS 视为普通数据段。从演示任务切换到临时任务之前,把指向描述规范数据段的描述符 Normal 的选择子 Normal_Sel 填到临时任务 TSS 中的各数据段寄存器(包括堆栈段寄存器)字段,于是在切换到临时任务时,作为恢复临时任务的现场,该选择子就被装到 DS 等数据段寄存器,对应的描述符 Normal 内的信息也就被装入到对应的高速缓冲寄存器中,达到为从临时任务切换到实模式作准备的目的。
我要说的:
解释这段程序中ebp+12,ebp+16,ret 8,下面是示意图
![](http://img.blog.csdn.net/20150417133745667)
ret 8先执行ret,取出栈的返回地址(段选择子和偏移)加载后add sp,8废除外层堆栈的8个字节(两个参数)。
运行结果:
利用段间转移指令 JMP 或者段间调用指令 CALL,通过任务门或直接通过任务状态段,可以切换到别的任务。此外,在中断/异常或者执行 IRET 指令时也可能发生任务切换。需要注意的是,因为 RET 指令的目标地址只能使用代码段描述符,所以,不能通过 RET指令实现任务切换。
1.直接通过TSS进行任务切换
段间转移指令 JMP 或段间调用指令 CALL 所含指针的选择子指示一个可用任务状态段 TSS 描述符时,正常情况下就发生从当前任务到由该可用 TSS 对应任务(目标任务)的切换。目标任务的入口点由目标任务 TSS 内的 CS 和 EIP 字段所规定的指针确定。这样的 JMP或 CALL 指令内的偏移被丢弃。另外,对于段间调用指令 CALL,若目标选择子指示一 TSS 段描述符或任务门时,则返回地址和外层栈指针并不压入堆栈。
处理器采用与访问数据段相同的特权级规则控制对 TSS 段描述符的访问。TSS 段描述符的 DPL 规定了访问该描述符的最外层特权级,只有在相同级别或更内层级别的程序才可以访问它。同时,还要求指示它的选择子的 RPL 必须满足 RPL<=TSS 的 DPL 的条件。当这些条件满足时,就开始任务切换。
2.通过任务门进行任务切换
任务门内的选择子指示某个任务的TSS描述符。当段间转移指令JMP或段间调用指令CALL所含指针的选择子指示一个任务门时,正常情况下就发生任务切换,即从当前任务切换到由任务门内的选择子所指示的 TSS 描述符对应的任务(目标任务)。这样的 JMP 或 CALL指令内的偏移被丢弃;任务门内的偏移也无意义。
处理器采用与访问数据段相同的特权级规则控制对任务门的访问。任务门的 DPL 规定了访问该任务门的最外层特权级,只有在同级或更内层级别的程序才可以访问它。同时,还要求指示任务门的选择子 RPL 必须满足 RPL<=任务门的 DPL 的条件。在这些条件满足时,再检查任务门内的选择子,要求该选择子指示 GDT 中的可用的 TSS 描述符。对于任务门所指向的 TSS 描述符的 DPL 不进行特权级检查。检查通过以后,就开始任务切换。
3.任务切换过程
根据指示目标任务 TSS 描述符的选择子进行任务切换的一般过程如下:
第一,测试目标任务状态段的界限。TSS 用于保存任务的各种状态信息,不同的任务,TSS 中可以有数量不等的其他信息,根据任务状态段的基本格式,TSS 的界限应大于或等于 103(104-1)。
第二,把寄存器现场保存到当前任务的 TSS。把通用寄存器、段寄存器、EIP 及 EFLAGS 的当前值保存到当前 TSS 中。保存的 EIP
值是返回地址,指向引起任务切换指令的下一条指令。但不把 LDTR 和 CR3 的内容保存到 TSS 中。
第三,把指示目标任务 TSS 的选择子装入 TR 寄存器中。同时把对应 TSS 的描述符装入 TR 的高速缓冲寄存器中。此后,当前任务
改称为原任务,目标任务改称为当前任务。
第四,基本恢复当前任务(目标任务)的寄存器现场。根据保存在 TSS 中的内容,恢复各通用寄存器、段寄存器、EFLAGS 及 EIP。
在装入寄存器的过程中,为了能正确地处理可能发生的异常,只把对应选择子装入各段寄存器。此时选择子的 P 位为 0。还装载 CR3 寄存器。
第五,进行链接处理。如果需要链接,那么将指向原任务 TSS 的选择子写入当前任务 TSS 的链接字段,把当前任务 TSS 描述符类型改为“忙”(并不修改原任务状态段描述符的“忙”位),并将标志寄存器 EFLAGS 中的 NT 位置 1,表示是嵌套任务。如果需要解链,那么把原任务TSS描述符类型改为“可用”。如果无解链处理,那么将原任务TSS描述符类型置为“可用”,当前任务TSS描述符类型置为“忙”。由于
JMP 指令引起的任务切换不实施链接/解链处理;由 CALL 指令、中断、IRET 指令引起的任务切换要实施链接/解链处理。
第六,把 CR0 中的 TS 标志置为 1,这表示已发生过任务切换,在当前任务使用协处理器指令时,产生自陷。由自陷处理程序完成有关协处理器现场的保存和恢复。这有利于快速地进行任务切换。
第七,把 TSS 中的 CS 选择子的 RPL 作为当前任务特权级设置为 CPL。又因为装入 CS 高速缓冲寄存器时要检测 CPL=代码段描述符的 DPL,所以 TSS 中的选择子所指示的代码段描述符的 DPL 必须等于该选择子的 RPL。任务切换可以在一个任务的任何特权级发生,并且可以切换到另一任务的任何特权级。
第八,装载 LDTR 寄存器。一个任务可以有自己的 LDT,也可以没有。当任务没有 LDT 时,TSS 中 LDT 选择子为 0。如果 TSS 中LDT 选择子非空,则从 GDT 中读出对应的 LDT 描述符,在经过测试后,把所读的 LDT 描述符装入 LDTR 高速缓冲寄存器。如果 LDT选择子为空,则将 LDT 的存在位置为 0,表明任务不使用 LDT。
第九,装载代码段寄存器 CS、堆栈段寄存器 SS 和各数据段寄存器及其高速缓冲寄存器。在装入代码段高速缓存之前,也要进行特权检查,处理器调整 TSS 中的 CS 选择子的 RPL=0,装入之后,调整 CS 的 RPL 等于目标代码段的 DPL。堆栈段使用的是 TSS 中的 SS和 SP 字段的值,而不是使用内层栈保存区中的指针,即使发生了向内层特权级的变换。这与任务内的通过调用门的转移不同。
第十,把调试寄存器 DR7 中的局部启用位置为 0,以清除局部于原任务的各个断点和方式。
4.关于任务状态和嵌套的说明
需要注意的是任务切换不能递归
在段间转移指令 JMP 引起任务切换时,不实施链接,不导致任务的嵌套。它要求目标任务是可用任务。切换过程中把原任务置为“可用”,目标任务置为“忙”。
在段间调用指令 CALL 引起任务切换时,实施链接,导致任务的嵌套。它要求目标任务是可用的任务。在切换过程中把目标任务置为“忙”,原任务仍保持“忙”;标志寄存器 EFLAGS 中的 NT 位被置为 1,表示任务是嵌套任务。
在由中断异常引起任务切换时,实施链接,导致任务的嵌套。要求目标任务是可用的任务。在切换过程中把目标任务置为“忙”,原任务仍保持“忙”;标志寄存器 EFLAGS 中的 NT 位被置为 1,表示任务是嵌套任务。
在执行 IRET 指令时引起任务切换,那么实施解链。要求目标任务是忙的任务。在切换过程中把原任务置为“可用”,目标任务仍保持“忙”。关于中断/异常如何引起任务切换和指令 IRET 如何考虑任务切换的内容将在后面的文章中论述。
二、演示任务切换的实例
下面给出一个用于演示任务切换的实例。该实例的逻辑功能是在切换后显示原任务的挂起点(EIP)的值。该实例演示内容包括:直接通过 TSS 段的任务切换,通过任务门的任务切换,任务内特权级的变换及参数传递。
1.实现步骤
为了达到任务切换和特权级变换的目的,该实例在保护模式下涉及到两个任务,一个任务为临时任务,另一个任务称为演示任务。演示任务的功能是演示通过调用门实现特权级的变换和堆栈间参数的自动复制。临时任务和演示任务配合展示任务切换实例实现步骤如下:
(1)实模式下初始化;
(2)切换到保护模式;
(3)设置 TR 对应临时任务,特权级为 0;
(4)直接切换到演示任务,演示任务的特权级为 2;(5)把入口参数压入堆栈,经调用门进入显示信息子程序,显示信息子程序的特权级为 0;
(6)从堆栈中取出入口参数并处理;
(7)从显示信息子程序返回特权级为 2 的演示代码段;
(8)为切换回实模式作部分准备工作;
(9)经任务门切换到特权级为 0 的临时任务;
(10)准备返回实模式;
(11)切换到实模式;
(12)实模式下的恢复工作。
在任务切换时,把原任务的现场保存到 TR 所指示的 TSS 内,然后再把指向目标任务的 TSS 描述符的选择子装入 TR,所以在从临
时任务切换到演示任务之前,要把指向临时任务 TSS 描述符的选择子装入 TR。通过把演示任务的 TSS 初始化成恢复点在特权级 2 的代码段,使得从临时任务切换到演示任务后,当前特权级 CPL=2。
2.源程序组织和清单
实例五由如下部分组成:
(1)全局描述符表 GDT。GDT 含有演示任务的 TSS 描述符和 LDT 段描述符,还含有临时任务 TSS 描述符和临时任务的代码段描述符,此外,还含有子程序代码段描述符、规范数据段描述符和视频缓冲区段描述符。
(2)演示任务的 TSS。以根据演示要求初始化。
(3)演示任务的 LDT 段。它含有演示任务的 0 级和 2 级堆栈段描述符、代码段和数据段描述符、分别以数据段方式描述 LDT 和临时任务 TSS 的数据段描述符、以及指向子程序的调用门和指向临时任务的任务门。
(4)演示任务的 0 级和 2 级堆栈段。32 位段,特权级分别为 0 和 2。(5)演示任务数据段。32 位段,特权级 3。
(6)子程序代码段。32 位代码段,特权级 0。(7)演示任务代码段。32 位代码段,特权级 2。
(8)临时任务的 TSS 段。未初始化。
(9)临时任务代码段。16 位代码段,特权级 0。
(10)实模式下的数据和代码段。该实例的逻辑功能是在切换后显示原任务的挂起点(EIP)的值。
源程序清单如下:
;windows ;16位偏移的段间直接转移指令的宏定义(在16位代码段中使用) ;---------------------------------------------------------------------------- JUMP16 MACRO Selector,Offsetv DB 0eah ;操作码 DW Offsetv ;16位偏移量 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;32位偏移的段间直接转移指令的宏定义(在32位代码段中使用) ;---------------------------------------------------------------------------- COMMENT <JUMP32> JUMP32 MACRO Selector,Offsetv DB 0eah ;操作码 DD Offsetv DW Selector ;段值或段选择子 ENDM <JUMP32> ;------------------------------------------------- JUMP32 MACRO Selector,Offsetv DB 0eah ;操作码 DW Offsetv DW 0 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;门描述符结构类型定义 ;---------------------------------------------------------------------------- Gate STRUC OffsetL DW 0 ;32位偏移的低16位 Selector DW 0 ;选择子 DCount DB 0 ;双字计数 GType DB 0 ;类型 OffsetH DW 0 ;32位偏移的高16位 Gate ENDS ;---------------------------------------------------------------------------- ;16位偏移的段间调用指令的宏定义(在16位代码段中使用) ;---------------------------------------------------------------------------- CALL16 MACRO Selector,Offsetv DB 9ah ;操作码 DW Offsetv ;16位偏移量 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;32位偏移的段间调用指令的宏定义(在32位代码段中使用) ;---------------------------------------------------------------------------- COMMENT <CALL32> CALL32 MACRO Selector,Offsetv DB 9ah ;操作码 DD Offsetv DW Selector ;段值或段选择子 ENDM <CALL32> ;------------------------------------------------- CALL32 MACRO Selector,Offsetv DB 9ah ;操作码 DW Offsetv DW 0 DW Selector ;段值或段选择子 ENDM ;---------------------------------------------------------------------------- ;存储段描述符结构类型定义 ;---------------------------------------------------------------------------- Desc STRUC LimitL DW 0 ;段界限(BIT0-15) BaseL DW 0 ;段基地址(BIT0-15) BaseM DB 0 ;段基地址(BIT16-23) Attributes DB 0 ;段属性 LimitH DB 0 ;段界限(BIT16-19)(含段属性的高4位) BaseH DB 0 ;段基地址(BIT24-31) Desc ENDS ;---------------------------------------------------------------------------- ;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器) ;---------------------------------------------------------------------------- PDesc STRUC Limit DW 0 ;16位界限 Base DD 0 ;32位基地址 PDesc ENDS ;---------------------------------------------------------------------------- ;任务状态段结构类型定义 ;---------------------------------------------------------------------------- TSS STRUC TRLink DW 0 ;链接字段 DW 0 ;不使用,置为0 TRESP0 DD 0 ;0级堆栈指针 TRSS0 DW 0 ;0级堆栈段寄存器 DW 0 ;不使用,置为0 TRESP1 DD 0 ;1级堆栈指针 TRSS1 DW 0 ;1级堆栈段寄存器 DW 0 ;不使用,置为0 TRESP2 DD 0 ;2级堆栈指针 TRSS2 DW 0 ;2级堆栈段寄存器 DW 0 ;不使用,置为0 TRCR3 DD 0 ;CR3 TREIP DD 0 ;EIP TREFlag DD 0 ;EFLAGS TREAX DD 0 ;EAX TRECX DD 0 ;ECX TREDX DD 0 ;EDX TREBX DD 0 ;EBX TRESP DD 0 ;ESP TREBP DD 0 ;EBP TRESI DD 0 ;ESI TREDI DD 0 ;EDI TRES DW 0 ;ES DW 0 ;不使用,置为0 TRCS DW 0 ;CS DW 0 ;不使用,置为0 TRSS DW 0 ;SS DW 0 ;不使用,置为0 TRDS DW 0 ;DS DW 0 ;不使用,置为0 TRFS DW 0 ;FS DW 0 ;不使用,置为0 TRGS DW 0 ;GS DW 0 ;不使用,置为0 TRLDTR DW 0 ;LDTR DW 0 ;不使用,置为0 TRTrip DW 0 ;调试陷阱标志(只用位0) TRIOMap DW $+2 ;指向I/O许可位图区的段内偏移 DB 0ffh ;I/O许可位图结束标志 TSS ENDS ;---------------------------------------------------------------------------- ;存储段描述符类型值说明 ;---------------------------------------------------------------------------- D32 EQU 40h ;32位代码段标志 ATDR EQU 90h ;存在的只读数据段类型值 ATDW EQU 92h ;存在的可读写数据段属性值 ATDWA EQU 93h ;存在的已访问可读写数据段类型值 ATCE EQU 98h ;存在的只执行代码段属性值 ATCER EQU 9ah ;存在的可执行可读代码段属性值 ;---------------------------------------------------------------------------- ;系统段描述符类型值说明 ;---------------------------------------------------------------------------- ATLDT EQU 82h ;局部描述符表段类型值 ;---------------------------------------------------------------------------- ;DPL值说明 ;---------------------------------------------------------------------------- DPL0 EQU 00h ;DPL=0 DPL1 EQU 20h ;DPL=1 DPL2 EQU 40h ;DPL=2 DPL3 EQU 60h ;DPL=3 ;---------------------------------------------------------------------------- ;RPL值说明 ;---------------------------------------------------------------------------- RPL0 EQU 00h ;RPL=0 RPL1 EQU 01h ;RPL=1 RPL2 EQU 02h ;RPL=2 RPL3 EQU 03h ;RPL=3 ;---------------------------------------------------------------------------- ;---------------------------------------------------------------------------- ;其它常量值说明 ;---------------------------------------------------------------------------- TIL EQU 04h ;TI=1(局部描述符表标志) AT386TSS EQU 89h ;可用386任务状态段类型值 AT386CGate EQU 8ch ;386调用门类型值 ATTaskGate EQU 85h ;任务门类型值 ;---------------------------------------------------------------------------- .386p GDTSeg SEGMENT PARA USE16 ;全局描述符表数据段(16位) ;---------------------------------------------------------------------------- ;全局描述符表 GDT LABEL BYTE ;空描述符 DUMMY Desc <> ;规范段描述符及选择子 Normal Desc <0ffffh,,,ATDW,,> Normal_Sel = Normal-GDT ;视频缓冲区段描述符(DPL=3)及选择子 VideoBuf Desc <0ffffh,8000h,0bh,ATDW+DPL3,,> Video_Sel = VideoBuf-GDT ;---------------------------------------------------------------------------- EFFGDT LABEL BYTE ;演示任务的局部描述符表段的描述符及选择子 DemoLDTab Desc <DemoLDTLen-1,DemoLDTSeg,,ATLDT,,> DemoLDT_Sel = DemoLDTab-GDT ;演示任务的任务状态段描述符及选择子 DemoTSS Desc <DemoTSSLen-1,DemoTSSSeg,,AT386TSS,,> DemoTSS_Sel = DemoTSS-GDT ;临时任务的任务状态段描述符及选择子 TempTSS Desc <TempTSSLen-1,TempTSSSeg,,AT386TSS+DPL2,,> TempTSS_Sel = TempTSS-GDT ;临时代码段描述符及选择子 TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,> TempCode_Sel = TempCode-GDT ;子程序代码段描述符及选择子 SubR Desc <SubRLen-1,SubRSeg,,ATCE,D32,> SubR_Sel = SubR-GDT ;---------------------------------------------------------------------------- GDNum = ($-EFFGDT)/(SIZE Desc) ;需处理基地址的描述符个数 GDTLen = $-GDT ;全局描述符表长度 ;---------------------------------------------------------------------------- GDTSeg ENDS ;全局描述符表段定义结束 ;---------------------------------------------------------------------------- DemoLDTSeg SEGMENT PARA USE16 ;局部描述符表数据段(16位) ;---------------------------------------------------------------------------- DemoLDT LABEL BYTE ;局部描述符表 ;0级堆栈段描述符(32位段)及选择子 DemoStack0 Desc <DemoStack0Len-1,DemoStack0Seg,,ATDW,D32,> DemoStack0_Sel = DemoStack0-DemoLDT+TIL ;2级堆栈段描述符(32位段)及选择子 DemoStack2 Desc <DemoStack2Len-1,DemoStack2Seg,,ATDW+DPL2,D32,> DemoStack2_Sel = DemoStack2-DemoLDT+TIL+RPL2 ;演示任务代码段描述符(32位段,DPL=2)及选择子 DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE+DPL2,D32,> DemoCode_Sel = DemoCode-DemoLDT+TIL+RPL2 ;演示任务数据段描述符(32位段,DPL=3)及选择子 DemoData Desc <DemoDataLen-1,DemoDataSeg,,ATDW+DPL3,D32,> DemoData_Sel = DemoData-DemoLDT+TIL ;把LDT作为普通数据段描述的描述符(DPL=2)及选择子 ;ToDLDT Desc <DemoLDTLen-1,DemoLDTSeg,,ATDW+DPL2,,> ;ToDLDT_Sel = ToDLDT-DemoLDT+TIL ;把TSS作为普通数据段描述的描述符(DPL=2)及选择子 ToTTSS Desc <TempTSSLen-1,TempTSSSeg,,ATDW+DPL2,,> ToTTSS_Sel = ToTTSS-DemoLDT+TIL ;---------------------------------------------------------------------------- DemoLDNum = ($-DemoLDT)/(SIZE Desc) ;需处理基地址的LDT描述符数 ;---------------------------------------------------------------------------- ;指向子程序SubRB代码段的调用门(DPL=3)及选择子 ToSubR Gate <SubRB,SubR_Sel,,AT386CGate+DPL3,> ToSubR_Sel = ToSubR-DemoLDT+TIL+RPL2 ;指向临时任务Temp的任务门(DPL=3)及选择子 ToTempT Gate <,TempTSS_Sel,,ATTaskGate+DPL3,> ToTempT_Sel = ToTempT-DemoLDT+TIL ;---------------------------------------------------------------------------- DemoLDTLen = $-DemoLDT ;---------------------------------------------------------------------------- DemoLDTSeg ENDS ;局部描述符表段定义结束 ;---------------------------------------------------------------------------- DemoTSSSeg SEGMENT PARA USE16 ;任务状态段TSS ;---------------------------------------------------------------------------- DD 0 ;链接字 DD DemoStack0Len ;0级堆栈指针 DW DemoStack0_Sel,0 ;0级堆栈选择子 DD 0 ;1级堆栈指针(实例不使用) DW 0,0 ;1级堆栈选择子(实例不使用) DD 0 ;2级堆栈指针 DW 0,0 ;2级堆栈选择子 DD 0 ;CR3 DW DemoBegin,0 ;EIP DD 0 ;EFLAGS DD 0 ;EAX DD 0 ;ECX DD 0 ;EDX DD 0 ;EBX DD DemoStack2Len ;ESP DD 0 ;EBP DD 0 ;ESI DD 320 ;EDI DW Video_Sel,0 ;ES DW DemoCode_Sel,0 ;CS DW DemoStack2_Sel,0 ;SS DW DemoData_Sel,0 ;DS DW ToDLDT_Sel,0 ;FS DW ToTTSS_Sel,0 ;GS DW DemoLDT_Sel,0 ;LDTR DW 0 ;调试陷阱标志 DW $+2 ;指向I/O许可位图 DB 0ffh ;I/O许可位图结束标志 DemoTSSLen = $-DemoTSSSeg ;---------------------------------------------------------------------------- DemoTSSSeg ENDS ;任务状态段TSS结束 ;---------------------------------------------------------------------------- DemoStack0Seg SEGMENT PARA USE32 ;演示任务0级堆栈段(32位段) DemoStack0Len = 1024 DB DemoStack0Len DUP(0) DemoStack0Seg ENDS ;演示任务0级堆栈段结束 ;---------------------------------------------------------------------------- DemoStack2Seg SEGMENT PARA USE32 ;演示任务2级堆栈段(32位段) DemoStack2Len = 512 DB DemoStack2Len DUP(0) DemoStack2Seg ENDS ;演示任务2级堆栈段结束 ;---------------------------------------------------------------------------- DemoDataSeg SEGMENT PARA USE32 ;演示任务数据段(32位段) Message DB 'Value=',0 DemoDataLen = $-DemoDataSeg DemoDataSeg ENDS ;演示任务数据段结束 ;---------------------------------------------------------------------------- SubRSeg SEGMENT PARA USE32 ;子程序代码段(32位) ASSUME CS:SubRSeg ;---------------------------------------------------------------------------- SubRB PROC FAR push ebp mov ebp,esp pushad ;保护现场 mov esi,DWORD PTR [ebp+12] ;从0级栈中取出显示串偏移 mov ah,4ah ;设置显示属性 jmp SHORT SubR2 SubR1: stosw SubR2: lodsb or al,al jnz SubR1 mov ah,4eh ;设置显示属性 mov edx,DWORD PTR [ebp+16] ;从0级栈中取出显示值 mov ecx,8 SubR3: rol edx,4 mov al,dl call HToASCII stosw loop SubR3 popad pop ebp ret 8 SubRB ENDP ;---------------------------------------------------------------------------- HToASCII PROC and al,0fh add al,90h daa adc al,40h daa ret HToASCII ENDP ;---------------------------------------------------------------------------- SubRLen = $-SubRSeg SubRSeg ENDS ;子程序代码段结束 ;---------------------------------------------------------------------------- DemoCodeSeg SEGMENT PARA USE32 ;演示任务的32位代码段 ASSUME CS:DemoCodeSeg,DS:DemoDataSeg ;---------------------------------------------------------------------------- DemoBegin PROC FAR ;把要复制的参数个数置入调用门 mov BYTE PTR fs:ToSubR.DCount,2 ;向2级堆栈中压入参数 push DWORD PTR gs:TempTask.TREIP push OFFSET Message ;通过调用门调用SubRB CALL32 ToSubR_Sel,0 ;把指向规范数据段描述符的选择子填入临时任务TSS ASSUME DS:TempTSSSeg push gs pop ds mov ax,Normal_Sel mov WORD PTR TempTask.TRDS,ax mov WORD PTR TempTask.TRES,ax mov WORD PTR TempTask.TRFS,ax mov WORD PTR TempTask.TRGS,ax mov WORD PTR TempTask.TRSS,ax ;通过任务门切换到临时任务 JUMP32 ToTempT_Sel,0 jmp DemoBegin DemoBegin ENDP DemoCodeLen = $-DemoCodeSeg ;---------------------------------------------------------------------------- DemoCodeSeg ENDS ;演示任务的32位代码段结束 ;---------------------------------------------------------------------------- TempTSSSeg SEGMENT PARA USE16 ;临时任务的任务状态段TSS TempTask TSS <> TempTSSLen = $-TempTSSSeg TempTSSSeg ENDS ;---------------------------------------------------------------------------- TempCodeSeg SEGMENT PARA USE16 ;临时任务的代码段 ASSUME CS:TempCodeSeg ;---------------------------------------------------------------------------- Virtual PROC FAR mov ax,TempTSS_Sel ;装载TR ltr ax JUMP16 DemoTSS_Sel,0 ;直接切换到演示任务 clts ;清任务切换标志 mov eax,cr0 ;准备返回实模式 and al,11111110b mov cr0,eax JUMP16 <SEG Real>,<OFFSET Real> Virtual ENDP ;---------------------------------------------------------------------------- TempCodeSeg ENDS ;============================================================================ RDataSeg SEGMENT PARA USE16 ;实方式数据段 VGDTR PDesc <GDTLen-1,> ;GDT伪描述符 SPVar DW ? ;用于保存实方式下的SP SSVar DW ? ;用于保存实方式下的SS RDataSeg ENDS ;---------------------------------------------------------------------------- RCodeSeg SEGMENT PARA USE16 ASSUME CS:RCodeSeg,DS:RDataSeg,ES:RDataSeg ;---------------------------------------------------------------------------- Start PROC mov ax,RDataSeg mov ds,ax cld call InitGDT ;初始化全局描述符表GDT mov ax,DemoLDTSeg mov fs,ax mov si,OFFSET DemoLDT mov cx,DemoLDNum call InitLDT ;初始化局部描述符表LDT mov SSVar,ss mov SPVar,sp lgdt FWORD PTR VGDTR ;装载GDTR并切换到保护方式 cli mov eax,cr0 or al,1 mov cr0,eax JUMP16 <TempCode_Sel>,<OFFSET Virtual> Real: mov ax,RDataSeg mov ds,ax lss sp,DWORD PTR SPVar ;又回到实方式 sti mov ax,4c00h int 21h Start ENDP ;---------------------------------------------------------------------------- InitGDT PROC push ds mov ax,GDTSeg mov ds,ax mov cx,GDNum mov si,OFFSET EFFGDT InitG: mov ax,[si].BaseL movzx eax,ax shl eax,4 shld edx,eax,16 mov WORD PTR [si].BaseL,ax mov BYTE PTR [si].BaseM,dl mov BYTE PTR [si].BaseH,dh add si,SIZE Desc loop InitG pop ds mov bx,16 mov ax,GDTSeg mul bx mov WORD PTR VGDTR.Base,ax mov WORD PTR VGDTR.Base+2,dx ret InitGDT ENDP ;---------------------------------------------------------------------------- ;入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数 ;---------------------------------------------------------------------------- InitLDT PROC mov ax,WORD PTR fs:[si].BaseL movzx eax,ax shl eax,4 shld edx,eax,16 mov WORD PTR fs:[si].BaseL,ax mov BYTE PTR fs:[si].BaseM,dl mov BYTE PTR fs:[si].BaseH,dh add si,SIZE Desc loop InitLDT ret InitLDT ENDP ;---------------------------------------------------------------------------- RCodeSeg ENDS END Start
程序中部分片段的背景和实现方式在前面的实例中做过介绍,下面只要就任务切换和通过门调用实现任务内特权级变换时参数的复制等情形做些说明:
(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 内的信息也就被装入到对应的高速缓冲寄存器中,达到为从临时任务切换到实模式作准备的目的。
我要说的:
;---------------------------------------------------------------------------- SubRB PROC FAR push ebp mov ebp,esp pushad ;保护现场 mov esi,DWORD PTR [ebp+12] ;从0级栈中取出显示串偏移 mov ah,4ah ;设置显示属性 jmp SHORT SubR2 SubR1: stosw SubR2: lodsb or al,al jnz SubR1 mov ah,4eh ;设置显示属性 mov edx,DWORD PTR [ebp+16] ;从0级栈中取出显示值 mov ecx,8 SubR3: rol edx,4 mov al,dl call HToASCII stosw loop SubR3 popad pop ebp ret 8 SubRB ENDP
解释这段程序中ebp+12,ebp+16,ret 8,下面是示意图
ret 8先执行ret,取出栈的返回地址(段选择子和偏移)加载后add sp,8废除外层堆栈的8个字节(两个参数)。
运行结果:
相关文章推荐
- 《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(一)
- 《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(二)
- 《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(三)
- 《Orange's 一个操作系统的实现》学习笔记--特权级代码段之间的转移(四)
- 《Orange's 一个操作系统的实现》学习笔记--LDT
- 《Orange'S:一个操作系统的实现》学习笔记(四)
- 《Orange's 一个操作系统的实现》学习笔记--分页机制
- 《Orange's 一个操作系统的实现》学习笔记--一个简单的引导扇区
- 《Orange's 一个操作系统的实现》学习笔记--保护模式理论初步(一)
- 《Orange's 一个操作系统的实现》学习笔记--保护模式理论初步(二)
- 《Orange's 一个操作系统的实现》学习笔记--实践认识保护模式
- 《Orange's 一个操作系统的实现》学习笔记--保护模式进阶
- 《Orange's 一个操作系统的实现》学习笔记(一) 实验环境搭建
- 《Orange'S:一个操作系统的实现》学习笔记(一)
- 《Orange'S:一个操作系统的实现》学习笔记(二)
- 《Orange'S:一个操作系统的实现》学习笔记(三)--bochs
- 《Orange'S:一个操作系统的实现》学习笔记(四)
- Orange's 一个操作系统的实现之笔记1
- 《orange'S一个操作系统的实现》 笔记
- 学习《orange's 一个操作系统的实现》准备