目录

1. 任务的概念与组成

1.1 任务的概念

1.2 多任务系统

1.3 任务的组成

1.3.1 局部描述符表LDT

1.3.2 任务状态段TSS

2. 任务控制块和TCB链表

2.1 为何需要任务控制块(TCB)

2.1.1 记录资源

2.1.2 状态跟踪

2.2 TCB的构成

2.3 将TCB插入TCB链表

2.3.1 分配TCB内存

2.3.2 TCB链表

2.3.3 append_to_tcb_link过程分析

3. 加载并启动应用程序

3.1 使用栈传递参数

3.1.1 程序实例

3.1.2 立即数压栈操作

3.1.3 立即数出栈时的栈平衡

3.2 段寄存器压栈操作

3.2.1 程序实例

3.2.2 段寄存器压栈数据尺寸

3.2.3 段寄存器出栈数据尺寸

3.3 栈的随机访问

3.3.1 栈随机访问的概念

3.3.2 栈随机访问的实现

3.4 创建任务的局部描述符表LDT

3.4.1 分配LDT内存

3.4.2 创建用户程序段描述符

3.5 安装任务LDT的描述符

3.5.1 回顾描述符类型

3.5.2 系统段描述符类型

3.5.3 安装LDT描述符

3.6 创建任务状态段TSS

3.6.1 分配TSS内存

3.6.2 设置TSS字段

3.7 安装任务TSS描述符

3.7.1 TSS段描述符类型

3.7.2 安装TSS描述符

3.8 load_relocate_program过程返回

3.8.1 带参数的ret指令

3.8.1 程序实例分析

3.9 加载用户程序后的内存布局

3.10 局部描述符表寄存器LDTR和加载任务寄存器TR

3.10.1 LDTR & TR寄存器

3.10.2 lldt & ltr指令

3.10.3 程序实例

3.11 上机验证

3.11.1 modebp调试命令

3.11.2 info gdt / info ldt / info tss调试命令


1. 任务的概念与组成

1.1 任务的概念

① 程序(Program)是记录在载体上的指令和数据,其正在执行中的一个副本,叫做任务(Task)。如果一个程序有多个副本正在内存中运行,那么他们就对应着多个任务

② 用户程序在运行时,需要内核的支持,我们将内核与运行中的用户程序看作一个整体

③ 任务是一个活动的概念

1.2 多任务系统

① 32位处理器是为多任务系统设计的,所谓多任务,是指能够同时执行两个以上程序的系统,处理器可以在多个任务之间周期性地切换和轮转

② 每个任务实际上包括两个部分:全局部分和私有部分,

a. 全局部分

所有任务共有的,含有操作系统的软件和库程序,以及可以调用的系统服务和数据

b. 私有部分

每个任务各自的数据和代码,与任务所要解决的具体问题有关,彼此间不相同

③ 在一个多任务系统中,会有很多任务在流转执行,正在执行中的那个任务,称为当前任务(Current Task)

1.3 任务的组成

1.3.1 局部描述符表LDT

① 内核和用户程序有各自的代码段、数据段和栈段,之前他们的段描述符都是存储在GDT中。虽然这样做是允许的,但是为了有效地在任务之间实施隔离,处理器建议每个任务都应当具有自己的描述符表,称为局部描述符表LDT(Local Descriptor Table),并且把专属于自己的那些段放在LDT中

② GDT只有一个,LDT可以有多个,每个任务一个

③ 与使用GDTR追踪GDT类似,处理器使用LDTR(LDT Register)追踪LDT。但是由于系统中LDT不止一个,因此LDTR只用于追踪当前任务的LDT

每当发生任务切换时,LDTR的内容被更新,以指向新任务的LDT

④ 和GDTR一样,LDTR也包含了32位线性基地址字段和16位段界限字段,用于指示当前LDT的位置和大小

⑤ 当向段寄存器加载段选择子时,段选择子中的bit [2]是表指示器(Table Indicator,TI),若TI = 0,从GDT加载段描述符(由GDTR跟踪);若TI = 1,从LDT加载段描述符(由LDTR跟踪)

说明1:全局空间与局部空间的实现

① 如上文所述,任务分为全局部分和私有部分,而任务实际上是在内存中运行的,因此所谓全局部分和私有部分,本质上是地址空间的划分,即全局地址空间和局部地址空

② 进一步地,地址空间的访问是依靠分段机制来进行的,因此全局地址空间是用GDT来指定的,局部地址空间则是通过LDT来定义的

③ 从用户程序的角度,任务的全局空间包含了操作系统的段,是由别人编写的,但是用户程序可以调用这些段的代码或者获取这些段中的数据

任务的局部空间的内容是用户程序程序员自己创建的,通常,任务会在自己的局部空间运行,当他需要操作系统提供的服务时,转入全局空间执行

说明2:Linux中的任务局部空间

① 通过LDT指定的局部空间实现了对线性地址的划分,每个任务的私有部分占用一段线性地址空间,即每个任务私有的线性地址空间是不同的

更正:这里认为LDT指定的局部空间实现了对线性地址的划分的理解,是错误的,这里没有考虑虚拟内存管理机制,下面的说明3进行了增补

② Linux显然没有听从处理器的"建议",并未使用LDT

其实在如Linux 0.11的早期版本中,也使用LDT让每个任务独自占有一段线性地址空间。但是在后续版本中,Linux不再使用LDT,而是让每个任务都占有4GB的线性地址空间,并通过分页机制为不同的任务建立映射。即每个任务的线性地址空间相同,但使用的物理地址不同

这样做是为了让Linux操作系统可以运行在其他绝大多数不支持分段机制的处理器上

③ 从根源上,GDT和LDT就是适用于以段为单位的内存管理机制,而随着分页机制成为内存管理的主流,GDT和LDT在操作系统中的功能势必会弱化

说明3:任务的虚拟地址空间有多大

① 任务的全局地址空间存储在GDT表,其中的每个描述符对应一个内存段,除去0号描述符不能使用,实际有8191个表项可使用。段的最大长度为4GB,因此一个任务的全局虚拟地址空间最大为(2^13 - 1)*2^32 = 32TB - 4GB(其实还要再减去内核与任务LDT & TSS的描述符表项)

② 任务的局部地址空间存储在LDT表,LDT的最大表项数也是8192,每个段最大长度4GB,因此一个任务的局部虚拟地址空间最大为32TB

③ 因此每个任务的总虚拟地址空间接近64TB,操作系统允许程序的编写者使用这些虚拟地址来访问内存(使用时先在段寄存器中加载段选择子),就像该程序真的拥有这么巨大的地址空间一样

④ 但是在一个只有32根地址线的处理器上,是不可能有这么大的物理,此时就使用基于段的交换机制来实现虚拟内存管理

1.3.2 任务状态段TSS

① 在多任务系统中,当发生任务切换时,必须保护旧任务的运行状态,或者说保护现场。保护的内容包括通用寄存器、段寄存器、栈指针寄存器ESP、指令指针寄存器EIP、状态寄存器EFLAGS等

当任务恢复执行时,用TSS中的内容恢复现场

② 为了保存任务的状态,并在下次重新执行时恢复他们,每个任务都应当用一个额外的内存区域保存相关信息,这就是任务状态段(Task State Segment,TSS)

③ TSS具有固定的格式,最小尺寸为104B,由处理器定义。处理器固件能够识别TSS中的每个元素,并在任务切换时读取其中的信息

④ 与LDT一样,处理器使用任务寄存器(Task Register,TR)指向当前任务的TSS。由于TR寄存器也只有一个,当任务切换发生时,TR寄存器的内容也会跟着指向新任务的TSS

说明1:本章中只关注TSS中的LDT段选择子和T位

① LDT段选择子

LDT本身也是一段内存,也有对应的描述符(与存储器段描述符不同的是,他是系统描述符)。根据处理器的要求,LDT的描述符需要安装在GDT中。而索引该LDT描述符的,就是LDT段选择子

② T位

在多任务环境中,如果TSS中的T位为1,则每次切换到该任务时,将触发一个调试异常中断。调试程序可以接管该中断,显示该任务的状态,并执行一些调试操作。如果T位为0,则不进行调试

说明2:任务切换过程中TR寄存器的使用

① 首先,处理器将当前任务的现场信息保存到由TR寄存器指向的TSS

② 然后,使TR寄存器指向新任务的TSS,并从新任务的TSS中恢复现场

说明3:多任务系统中GDTR / LDTR / TR寄存器的指向关系如下图所示

说明4:为什么GDT没有描述符 ?

①  LDT和TSS本质上都是一段内存,因此他们也有自己的系统描述符类型

② GDT本质上也是一段内存,但是GDT是全局唯一的,因此直接使用4B线性基地址 + 2B表界限值即可实现寻址

而LDT和TSS的个数是随任务个数而定的,因此处理器需要描述和记录所有LDT和TSS的内存段信息。其中,

a. 描述的方式就是构建相应的描述符

b. 记录的方式就是将描述符安装在GDT中

③ 这种不同体现在指令中,就是lgdt指令的参数是一个6B的内存单元,其中存储的是GDT线性基地址和表界限。但是lldt & ltr指令的参数是16位的寄存器或内存单元,其中存储的是段选择子

分析到这里,我们就可以总结一下X86体系结构中内存分段机制的使用方式。

在X86体系结构中,GDT、LDT、TSS以及程序的代码段、数据段和栈段等,本质上都是一个内存段,而所有的内存段均需要描述、记录和索引,其中,

a. 描述,就是使用描述符来描述内存段的线性基地址和段界限等特性(所有类型描述符固定为8B)

b. 记录,就是将描述符安装在GDT或者LDT表中,他们就是描述符的组织方式

c. 索引,就是使用段选择子在GDT或者LDT中索引相应的描述符,进而找到相应的内存段

其中GDT是一个特殊的内存段,由于他的全局唯一性,不需要构建描述符,而是直接用线性基地址 + 表界限来寻址

拓展1:硬件分段与逻辑分段

① 从上面的分析可以看出,X86体系结构是在硬件上支持内存分段机制,可以将程序中的逻辑分段(e.g. 代码段、数据段)在硬件层面上实现,即将逻辑分段映射到硬件分段中

比如程序中逻辑上的代码段在硬件层面就有对应的段描述符和段选择子,而程序中逻辑上的数据段在硬件上也有自己的段描述符和段选择子

② 在后续的X86-Linux操作系统实现中,绕过了X86的内存分段机制,仅使用内存分页机制,此时程序中的分段就仅具有逻辑上的意义,只是为了组织程序的不同部分而存在。在硬件层面,用户程序的代码段映射到4GB用户代码段描述符和段选择子;用户程序的数据段、栈段等映射到4GB用户数据段描述符和段选择子

拓展2:系统中任务的个数上限

根据上文所述,X86体系结构中,每个任务有自己的LDT和TSS,而LDT和TSS对应的描述符要存储到GDT中,也就是每个任务在GDT中要占用2个表项

而GDT最多有8192个表项,因此系统中同时存在的任务个数不能超过4096个(操作系统本身还要使用GDT表项)

2. 任务控制块和TCB链表

2.1 为何需要任务控制块(TCB)

2.1.1 记录资源

为了支持多任务和任务切换,需要将应用程序创建为一个任务。而每个任务都包含很多内容,比如指令和数据,局部描述符表LDT,任务状态段TSS等。他们都要占用内存空间,因此在创建任务时需要生成一张清单,对他们在内存中的地址和长度进行记录,以便在任务结束时回收其占用的资源

2.1.2 状态跟踪

同时,为了在任务之间进行切换和轮转,必须能追踪到所有正在运行的任务,记录他们的状态,或者根据他们的当前状态采取适当的操作

为了满足以上要求,内核为每个任务创建了一个内存区域,用来记录任务的信息和状态,这个内存区域称为任务控制块(Task Control Block,TCB)

说明:TCB的存在不是处理器的要求,而是操作系统为了方便管理任务而发明的

但其实可以发现,TCB在操作系统中是不可或缺的。没有TCB,我们很难跟踪系统中的所有任务,以及这些任务持有的私有资源(e.g. LDT & TSS)

2.2 TCB的构成

下图是本课程自定义的TCB结构,共70B,本章我们只关注红框中的部分

其中窄的格子,宽度是一个字(2B);宽的格子,宽度是一个双字(4B)

2.3 将TCB插入TCB链表

2.3.1 分配TCB内存

TCB也是一段内存空间,每个任务都有一个TCB,因此在每个任务创建时,都需要申请TCB所需的内存空间

使用内核例程段的allocate_memory过程进行内存分配,进入该过程时,ecx中保存的是要分配的内存字节数,此处为70B;退出该过程时,ecx中保存的是分配到的内存的起始地址

说明:TCB内存有没有建立描述符 ?

① 首先,X86保护模式下,所有的内存访问都是要经过段描述符的,TCB作为一段内存当然也不例外

② TCB内存的描述符就是之前建立的0 ~ 4GB数据段描述符

③ 我们也可以再建立一个段描述符,用来描述一个任务的TCB内存,只是在实践中没有必要

后文中将看到,任务的LDT和TSS所用内存也是调用allocate_memory过程分配得到的,也就是说,也可以用0 ~ 4GB数据段描述符来描述。但是由于X86体系结构的要求,需要为任务LDT和TSS所用内存也建立描述符,并且安装在GDT中,以便任务切换时使用

2.3.2 TCB链表

为了追踪系统中的所有任务,我们将每个任务的TCB串联起来,构成TCB链表

① 首先在内核数据段定义了tcb_chain标号,作为链表表头,用来指向第一个任务的TCB线性基地址。如果为0,则说明当前链表为空

② TCB结构中偏移为0的成员,用于记录下一个TCB的线性基地址,从而构成链表。所有任务都按照创建的先后顺序链接在一起,从tcb_chain开始,可以依次找到每一个任务

2.3.3 append_to_tcb_link过程分析

append_to_tcb_link过程用于将一个新的TCB链接到tcb_chain的表尾

上述代码的对应的流程图如下,

说明:代码中edx和eax是前后指针关系,eax在前,edx在后,当eax为0时,edx正好指向链表中最后一个TCB

3. 加载并启动应用程序

说明:本章主要说明新增部分,与上一章节中相同的读取磁盘,解析SALT和生成描述符等操作不再重复

3.1 使用栈传递参数

3.1.1 程序实例

① 上文中已经分配了任务的TCB内存,并将其加入TCB链表,下面调用load_relocate_program过程加载和重定位程序,并创建一个任务

② load_relocate_program过程有2个参数,分别是用户程序在硬盘上的起始逻辑扇区号和TCB的线性地址

③ 此处我们使用栈来传递参数

说明:使用栈而不是使用寄存器来传递参数,是X86体系结构中更标准的做法。因为寄存器数量有限,且还要在过程内部使用

3.1.2 立即数压栈操作

① X86处理器允许使用push指令压入立即数,但是只能压入3种尺寸的立即数

push imm8  ;push byte  0x55
push imm16 ;push word  0x55
push imm32 ;push dword 0x55

从上述示例可知,由于压入的是立即数,因此需要通过byte / word / dword来明确指定尺寸

② 立即数压栈时实际操作的数据尺寸,与处理器当前的默认操作尺寸有关

a. push imm8

b. push imm16

c. push imm32

可见push imm8时,实际压入的数据尺寸与默认操作尺寸相同;push imm16 / imm32则可以压入指定长度的数据

这是因为在编译代码时,编译器会根据编译时设置的环境添加操作数尺寸反转前缀,从而使得处理器在运行时可以在16位模式下可以操作32位操作数,在32位模式下可以操作16位操作数

上述结论,从编译结果分析,将更加直观

3.1.3 立即数出栈时的栈平衡

上文说明了在不同情况下立即数压栈时的数据尺寸,在立即数出栈时,必须使用相同尺寸的寄存器或内存来作为容器,以此保证栈的平衡

以如下代码为例,

push byte 0x55实际是压入一个双字(4B),因此出栈时需要双字长度的寄存器或者dword修改的内存来作为容器

如果此处出栈的指令改为pop dx,那么在编译时会添加操作数尺寸反转前缀,在运行时就会只出栈2B数据,就将破坏栈的平衡

3.2 段寄存器压栈操作

3.2.1 程序实例

在进入load_relocate_program过程后,将过程中会使用到的段寄存器压栈保存

表面上看段寄存器压栈是常见的操作,你大概认为他们是按照16位的长度压入,因为段寄存器的长度是16位的,但是实际上并不是这样的

3.2.2 段寄存器压栈数据尺寸

段寄存器的压栈操作包括如下指令,

push cs / pus ss / push ds / push es / push fs / ps gs

段寄存器压栈时实际操作的数据尺寸如下,

我们同样来看一下不同模式下的编译结果,

可见此处并未使用操作数尺寸反转前缀

3.2.3 段寄存器出栈数据尺寸

段寄存器的出栈操作包括如下指令,需要注意,没有pop cs操作,这是不合法的

段寄存器出栈时实际操作的数据尺寸如下,

3.3 栈的随机访问

3.3.1 栈随机访问的概念

栈的访问方式有2种,

① 隐式的

由处理器执行诸如push、pop、call、ret等指令时自动进行,隐式地栈访问使用ESP寄存器,此时依赖先进后出机制

② 显式的

不依赖先进后出机制,而是把栈看作一般的数据段,直接访问其中的任何内容,也就是栈随机访问

在这种方式下,一般使用BP / EBP寄存器。因为如果在指令中使用BP / EBP寄存器提供有效地址,默认的段寄存器是SS,因此也是指向当前栈段

3.3.2 栈随机访问的实现

在load_relocate_program过程中,会将esp的值传递给ebp,之后使用ebp就可以索引到通过栈传递给该过程的参数

在mov ebp, esp指令之后,ebp指向的栈布局如下图所示,

3.4 创建任务的局部描述符表LDT

3.4.1 分配LDT内存

在当前示例中,用户程序的段描述符将安装在LDT中,因此需要先分配LDT所需内存。此处分配160B,可以容纳20个段描述符

说明:LDT的初始界限值

对于新分配的LDT,是一张空表,长度为0。而LDT界限 = LDT长度 - 1(或者说LDT长度 = LDT界限 + 1,这点和GDT是相同的),因此初始界限值为0 - 1 = 0xffff(保留16位)

等后续向LDT中安装段描述符后,会根据实际描述符的个数来设置

3.4.2 创建用户程序段描述符

下面以用户程序头部段为例,说明内核如何为用户程序创建段描述符,共有如下3个步骤

3.4.2.1 生成段描述符

生成段描述符的过程与之前相同,向make_seg_descriptor过程提供段的起始线性地址、段界限和段属性即可

过程调用返回时,EDX : EAX中将存储8B的段描述符

3.4.2.2 在LDT中安装段描述符

与上一章不同,本章将用户程序的段描述符安装在任务的LDT中,而不是GDT中。我们来分析一下fill_descriptor_in_ldt过程的实现

可见在LDT中安装新的描述符与在GDT中安装的过程相同,只是最后生成的段选择子中将TI位置为1,即使用LDT

说明1:LDT初值界限值为0xffff的作用

当向LDT中安装第一个段描述符时,cx寄存器中存储的界限值为0xffff,inc cx后,cx = 0,正好指向LDT中首个描述符的偏移地址(这里其实利用了X86体系结构中对cx的操作不影响ecx的特性,作为对比,AArch64中对w0的操作是会影响x0的)

从这里可以看出,LDT中的0号槽位是可以使用的。作为对比,GDT的0号槽位不可使用

说明2:LDT的0号槽位可以使用

在X86处理器中,数值为0的选择子是无效的段选择子,不能使用。所以GDT表中的0号槽位不使用。因为索引为0,TI为0,RPL若也为0,则为一个全零的段选择子

因为LDT段选择子的TI位为1,所以即使描述符索引和RPL均为0,段选择子也不会为0,即不会是无效选择子,因此LDT的0号槽位可以使用

3.4.2.3 登记与回填段选择子

生成的段选择子会回填到加载的用户程序中,也会登记到TCB中

3.5 安装任务LDT的描述符

截至目前,任务的LDT已经分配并安装完毕,下面就是将其登记到GDT中

3.5.1 回顾描述符类型

X86体系结构中的描述符类型如下,

① 首先,无论是存储器的段还是系统的段,本质上都是一段内存

② 所谓系统的段,就是用于存储系统信息的段

③ LDT和TSS就属于系统的段,他们有自己的段描述符类型。根据X86体系结构的要求,LDT和TSS的段描述符需要存储在GDT中,结构如下图所示

3.5.2 系统段描述符类型

其中S位必须是0,表明是系统段描述符,具体类型由TYPE字段指定

说明:对于系统段描述符,D位和L位没有意义,一般置为0

3.5.3 安装LDT描述符

对照系统段描述符类型,LDT描述符的属性如下,

① TYPE = 0b0010,表示LDT描述符

② P = 1,表示该描述符对应的段在内存中

② G = 0,表示段界限以字节为单位

说明1:LDT段的最大尺寸是64KB

说明2:任务的LDT段选择子同时也被登记在TCB中

3.6 创建任务状态段TSS

3.6.1 分配TSS内存

TSS内存也通过allocate_memory过程分配,长度为104B。注意登记到TCB中的界限值为TSS长度减1

说明:TSS的长度最少为104B,也就是界限值至少是103B,任何小于该值的TSS,在执行任务切换时,都会引发处理器异常中断

3.6.2 设置TSS字段

本章示例中只使用TSS段中的2个字段

说明:在任务切换时,处理器需要使用TSS中登记的信息找到当前任务的LDT

3.7 安装任务TSS描述符

3.7.1 TSS段描述符类型

如之前所述,TSS也有自己的描述符,即TSS描述符。每个任务的TSS描述符也需要安装在GDT中

TSS描述符和LDT描述符的类型几乎一样,只有TYPE字段不同

说明:TSS段描述符TYPE中的B位

① TYPE字段中的B位是Busy的意思,这位不是固定的,而是由处理器根据任务的实时状态进行修改的

② 在创建TSS描述符时,应该将其清零,此时TYPE字段为1001

③ 在多任务环境中,多个任务轮流执行,当任务开始执行时,或者处于挂起状态时,由处理器固件,将相应任务的TSS描述符的B位置1,此时TYPE字段为1011

④ 在TSS段描述符中设置B位,并由处理器固件进行管理,就可以避免任务重入的情况。也就是当前任务在执行时,不能再次切换到当前任务

说明1:剧透一下,当call far或jmp far指令的操作数是TSS描述符选择子时,处理器执行任务切换操作

说明2:所谓任务的不可重入,就是指如果一个任务是当前任务,他可以切换到其他任务,但是不能自己切换到自己

3.7.2 安装TSS描述符

安装TSS描述符的操作与安装LDT描述符相同

3.8 load_relocate_program过程返回

3.8.1 带参数的ret指令

带参数的ret指令格式如下,

ret imm16
retf imm16

该指令执行的操作如下,

可见imm16用于指示在将控制返回到调用者之前,应当从栈中弹出多少字节的数据

3.8.1 程序实例分析

load_relocate_program过程的最后,通过ret 8实现过程调用的返回,同时清理调用过程前压入栈中的参数

下面进一步分析过程返回时的内存场景,在执行完pop es / pop ds / popad指令序列后,栈的状态如下图所示

如果此时仅调用ret指令,虽然可以实现过程调用的返回,但是之前压入栈中的参水会残留在栈中

此处使用ret 8指令,就可以在实现过程调用返回的同时,清理之前压栈的参数

说明:在上述示例中,也可以由过程的调用者来清理压栈的参数,在具体实现中,由谁来清理调用前压入栈中的参数,需要调用双方约定

一个典型的调用标准是stdcall,他规定参数从右向左入栈,且由过程在返回前出栈

3.9 加载用户程序后的内存布局

3.10 局部描述符表寄存器LDTR和加载任务寄存器TR

3.10.1 LDTR & TR寄存器

如上文所述,每个任务都有自己的指令、数据、LDT和TSS,因此在一个多任务系统中,就会有很多LDT和TSS

当一个任务开始执行时,处理器就需要找到与之对应的LDT和TSS。这就需要用到LDTR和TR寄存器,他们用于记录当前任务的LDT和TSS

LDTR和TR寄存器结构如下图所示,

选择器长度为16位,是寄存器的可见部分;后续的描述符高速缓存器对程序员不可见,是处理器为了加速寻址在内部设置的(与段描述符的高速缓存器功能一致)

说明:这里可以看出GDTR寄存器与这2个寄存器在使用方式上的区别

我们来回顾一下GDTR寄存器的结构,

① 由于GDT是全局唯一的,所以lgdt指令直接获取32位线性基地址 + 16位界限值

② LDT和TSS是随任务设置的且对应的描述符安装在GDT中,所以设置LDTR和TR时,使用的是选择子

3.10.2 lldt & ltr指令

lldt & ltr指令格式如下,

lldt r/m16 ;lldt dx;lldt [0x208d]ltr r/m16  ;ltr bx;ltr [0x208b]

其中r/m16中存储的是16位选择子

说明1:lldt指令执行过程

一旦LDTR的选择器部分发生变化,处理器就会根据该选择子将GDT中对应的描述符加载到描述符高速缓存器,之后LDTR就可以指向LDT所在的内存段了

说明2:ltr指令执行过程

ltr指令的执行过程与lldr指令类似,最终也是让TR寄存器可以指向TSS所在的内存段

3.10.3 程序实例

由于之前已经将LDT选择子和TSS选择子记录在TCB中,此处直接使用TCB中记录的值进行设置

说明:mov ds, [ecx+0x44]操作加载的就已经是LDT中的段描述符

因为内核创建的用户程序头部段选择子中的TI位为1,指向LDT,而当前所使用的LDT又记录在LDTR寄存器中(即当前的LDT已经生效)

3.11 上机验证

3.11.1 modebp调试命令

modebp命令可以在从实模式切换到保护模式时自动设置断点

可见断点设置在了MBR中jmp dword 0x0010:flush处,执行该命令后,处理器将进入保护模式

说明:此处需要在进入MBR之后再使用modebp设置断点,因为在BIOS阶段,处理器也会进入保护模式。如果在系统启动后就使用modebp设置断点,断点位置将在BIOS中

3.11.2 info gdt / info ldt / info tss调试命令

① 使用info gdt命令查看LDT和TSS段描述符信息

② 使用info ldt查看当前任务的LDT信息

③ 使用info tss查看当前任务的TSS信息

X86汇编语言从实模式到保护模式15:任务和任务的创建相关推荐

  1. x86汇编语言从实模式百度云_Intel x86 CPU 32位保护模式杂谈之任务切换 上

    目录: 什么是任务 任务由什么组成 任务门描述符是什么东东?有了TSS描述符为什么要有任务门描述符? 参考文献 什么是任务 任务(task)是处理器可以分配.执行.挂起的工作单位,笔者认为和我们操作系 ...

  2. 硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01

    本文是<x86汇编语言:从实模式到保护模式>(电子工业出版社)的读书实验笔记. 这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果. 需要的源码文件 第一个文件是加载程 ...

  3. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16...

    一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...

  4. 《x86汇编语言:从实模式到保护模式》视频来了

    <x86汇编语言:从实模式到保护模式>视频来了 很多朋友留言,说我的专栏<x86汇编语言:从实模式到保护模式>写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节. ...

  5. 《x86汇编语言:从实模式到保护模式》读书笔记之后记

    本来打算把整本书的读书笔记写完,可是由于有其他的计划(就叫做"B计划"吧)且优先级更高,所以我的读书笔记搁浅了.为了全力以赴执行B计划,我的博客要荒芜一段时间(我希望不要永远荒芜下 ...

  6. 处理器在实施任务切换时的操作——《x86汇编语言:从实模式到保护模式》读书笔记39

    处理器在实施任务切换时的操作--<x86汇编语言:从实模式到保护模式>读书笔记39 处理器可以通过以下四种方法实施任务切换: 1. call指令或者jmp指令的操作数是GDT内的某个TSS ...

  7. 任务切换——《x86汇编语言:从实模式到保护模式》读书笔记38

    任务切换--<x86汇编语言:从实模式到保护模式>读书笔记38 本文及后面的几篇博文是原书第15章的学习笔记. 本章依然使用第13章的主引导程序. 1. 协同式多任务与抢占式多任务 有两种 ...

  8. 任务切换的方法——《x86汇编语言:从实模式到保护模式》读书笔记37

    任务切换的方法--<x86汇编语言:从实模式到保护模式>读书笔记37 1. 中断门和陷阱门 在实模式下,内存最低端的1M是中断向量表,保存着256个中断处理过程的段地址和偏移.当中断发生时 ...

  9. 任务和特权级保护(五)——《x86汇编语言:从实模式到保护模式》读书笔记36

    任务和特权级保护(五)--<x86汇编语言:从实模式到保护模式>读书笔记36 修改后的代码,有需要的朋友可以去下载(c14_new文件夹).下载地址是: GitHub: https://g ...

  10. 任务和特权级保护(四)——《x86汇编语言:从实模式到保护模式》读书笔记35

    任务和特权级保护(四)--<x86汇编语言:从实模式到保护模式>读书笔记35 7. 正式进入用户程序的局部空间 67 mov ebx,message_1 68 call far [fs:P ...

最新文章

  1. 逻辑模型设计步骤-分析主题域
  2. linux的运维管理UNIT3
  3. java 向量上的坐标点_新高三知识点-点的平移公式
  4. [bzoj4825]:[Hnoi2017]单旋
  5. JSON合并补丁:JSON-P 1.1概述系列
  6. 电影《姜子牙》要被改编成游戏了 期待吗?
  7. git 服务器上新建项目
  8. oracle判断日期字符串格式,如何测试日期格式字符串是否为Oracle中的有效日期格式字符串...
  9. Necurs僵尸网络重操旧业发送垃圾邮件 影响股票市场
  10. 《ARM嵌入式Linux系统开发从入门到精通》勘误
  11. 数据库原理及应用(思维导图、索引、合集)
  12. 一杯茶的时间,上手 Django 框架开发
  13. html5 blockquote,HTML5 Blockquote引用区块使用实例
  14. What Android Is
  15. phalapi门店管理系统插件,门店erp系统
  16. java丶对数组值按首字母进行排序
  17. 今年晋升本没抱希望,已有绩效更好的同事将参加晋升,leader却临时让我也去答辩,怀疑自己被拉去陪跑,该怎么办?...
  18. Remix-IDE安装开发环境与使用文档(Windows环境)
  19. java中modifier_java中关于.lang.reflect.Modifier.isInterface()方法的实例详解
  20. Linux学习笔记35——特定权限设置(比group、user更细的设置)、身份切换(su、sudo)、密码验证过程详解(PAM)、批量创建用户

热门文章

  1. 鸿蒙电视是无线么,鸿蒙系统首秀,在自家设备上和普通电视大不相同赵崇带你走世界...
  2. linux让新生成的文件自动加锁,Linux_实用技巧一则 Linux系统对文件进行加锁,当有多个工作线程要同时对一 - phpStudy...
  3. P2617 Dynamic Rankings
  4. docker-compose搭建ghost博客系统
  5. Linux中的提权操作
  6. 西安python后端招聘_有大佬招 Python 后端初级人员吗?
  7. 把word地址做链接在线打开word
  8. php 数据 缓存,php终极数据缓存,比redis、GlobalData等快200倍以上,极致性能
  9. com.alibaba.fastjson.JSONException: can‘t create non-static inner class inst
  10. 解决The valid characters are defined in RFC 7230 and RFC 3986错误问题