27、任务和任务的创建


上一节:26、用户程序编程接口及其实现
下一节:28、特权级和特权级保护

01、任务的概念和组成

内核是对整个计算机系统进行管理,管理软件和硬件。内核可以加载用户程序、对用户程序进行重定位、用户程序终止后还可以回收用户程序的资源、内核为用户程序提供API。

将内核(操作系统)和用户程序看作一个整体:任务。
在这里插入图片描述
在一个系统中只有一个内核,但可以有多个用户程序,即组成多个任务。
在这里插入图片描述
GDT存放内核中的描述符,LDT(每个任务都有一个)存放每个任务中用户程序的描述符:
在这里插入图片描述
每个任务还需要一个任务状态段:TSS(Task Status Segment)
在这里插入图片描述
TSS用来保存一个任务的状态信息,从当前任务切换到其他任务执行时,需要保存当前任务的状态信息,包括每个段寄存器的值、指令指针寄存器的值、通用寄存器的值。当该任务重新执行时从这个TSS中取出相关参数即可。

02、使用任务控制块保存任务的基本信息

本节主要介绍了本章程序的结构,主要结构如下,本章程序有三个。

  • 用户程序:c13_app1.asm
    1、用户头部段
    2、用户数据段
    3、用户栈段
    4、用户代码段
    5、用户尾部段
  • 内核程序:c14_core.asm
    1、定义常量存放选择子;
    2、内核程序头部段;
    3、内核公共例程段,就是一些例程,和以前一样;
    4、内核数据段,一些文本信息、SALT等;
    5、内核代码段,有内核入口点start、创建任务控制块TCB(Task Control Block)记录任务的信息;
    6、内核尾部段;
  • 引导程序:c13_mbr0.asm
    1、创建必要的段描述符;
    2、进入保护模式;
    3、加载内核;
    4、创建内核的段描述符;
    5、使用jmp far指令跳转执行内核;

03、将任务控制块加入任务控制块链表

本节主要讲解内核代码段中的append_to_tcb_link,在内核数据段定义标号tcb_chain dd 0,是一个指针,用来保存第一个任务的任务控制块的线性基地址,称为头指针,为0表示链表为空即没有任务。

其中任务控制块的结构如下:
在这里插入图片描述
任务控制块链表:
在这里插入图片描述
清除TCB指针域:表明当前TCB是最有一个。
在这里插入图片描述
判断链表中是否为空,为空表示这是第一个TCB
在这里插入图片描述
若链表不为空,顺着链表找到最后一个TCB
在这里插入图片描述
找到最后一个TCB,指向新的TCB,并返回调用者:
在这里插入图片描述

04、通过栈传递例程参数和立即数的压栈指令

接上一节,在创建TCB之后,接着加载和重定位用户程序并创建一个任务。使用call load_relocate_program调用例程load_relocate_program,使用栈传递参数,此时的栈是在引导程序中定义的4K字节、基地址为0x7c00的栈。代码如下:

。。。
	push dword 50		;用户程序位于逻辑50扇区
	push ecx			;压入任务控制块起始线性地址 
	
	call load_relocate_program
。。。

其中立即数的压栈指令:
在这里插入图片描述
push imm8:
在这里插入图片描述
push imm16:
在这里插入图片描述
push imm32:
在这里插入图片描述
压栈可以不需要容器,即直接使用立即数,但是出栈需要有容器接着:
在这里插入图片描述
其中处理器使用32位操作尺寸,压入一个双字(4个字节),0x00000055。出栈使用32位寄存器EDX。在这里压栈和出栈的尺寸必须一致,否则会影响栈平衡。

05、段寄存器的压栈和出栈和栈的随机访问机制

接上一节,进入load_relocate_program例程之后进行段寄存器的压栈操作:

...
...
load_relocate_program:		;加载并重定位用户程序
	                        ;输入: PUSH 逻辑扇区号
	                        ;      PUSH 任务控制块基地址
	                        ;输出:无 
	pushad                  ;压入全部8个通用寄存器的值
	
	push ds
	push es
...
...

其中段寄存器的压栈操作:
在这里插入图片描述
段寄存器的出栈操作:pop cs指令非法
在这里插入图片描述
接着执行指令:

...
	mov ebp, esp		;记录esp的值到ebp中
...

此时栈的分布:
在这里插入图片描述
使用EBP记录ESP的值,用来访问栈中的参数,可以在不破坏栈顶指针ESP的情况下访问栈中的其他数据。
在这里插入图片描述
其中若使用BPEBP访问栈段,那么默认的栈就是SS

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

任务共有部分的描述符安装在GDT,私有部分安装在LDT中。

接上一节,在load_relocate_program例程中:申请创建LDT所需要的内存、加载用户程序 、
判断整个用户程序有尺寸、之后就开始建立用户头部段描述符、接着就使用例程fill_descriptor_in_ldt将描述符安装在LDT中。

具体看视频、看程序注释c14_core0.asm

07、在当前任务的LDT中安装描述符

接上一节,在fill_descriptor_in_ldt例程中运行。

其中,更新界限值:CX中计算的进位被丢弃,不会影响ECX的高16位。
在这里插入图片描述
其中,更新选择子的TI位为1,表示在LDT中:
在这里插入图片描述
因为0号槽位在GDT中是无效的,但是在LDT中,TI位为1,所以在LDT中0号槽位是有效的。

08、LDT描述符的格式和创建

描述符的分类:
在这里插入图片描述
GDT的线性基地址存放在GDTR寄存器中,LDTTSS的线性基地址存放在GDT中,即LDTTSS自己也有一个描述符,要安装在GDT中。
在这里插入图片描述
描述符中的S位TYPE字段:
在这里插入图片描述
在这里插入图片描述

  • S = 0:表示系统描述符,那么位22、21系统描述符来说没有意义,置0。
  • TYPE = 0010LDT描述符。

GDT中登记LDT描述符时:

。。。
	;在GDT中登记LDT描述符
	mov eax,[es:esi+0x0c]              ;LDT的起始线性地址
	movzx ebx,word [es:esi+0x0a]       ;LDT段界限
	mov ecx,0x00008200                 ;LDT描述符,特权级0
	call sys_routine_seg_sel:make_seg_descriptor
	call sys_routine_seg_sel:set_up_gdt_descriptor
	mov [es:esi+0x10],cx               ;登记LDT选择子到TCB中
。。。

其中:
在这里插入图片描述

  • S = 0:表示系统描述符;
  • TYPE = 0010:表示LDT描述符;
  • P = 1:表示段存在;
  • G = 0:表示段界限以字节为单位。

09、创建任务状态段TSS

接上一节,本节创建TSS,代码如下:

。。。
	;创建用户程序的TSS
	mov ecx,104                        ;tss的基本尺寸
	mov [es:esi+0x12],cx              
	dec word [es:esi+0x12]             ;登记TSS界限值到TCB 
	call sys_routine_seg_sel:allocate_memory
	mov [es:esi+0x14],ecx              ;登记TSS基地址到TCB
	
	mov dx,[es:esi+0x10]               ;登记任务的LDT选择子
	mov [es:ecx+96],dx                 ;到TSS中
	
	mov word [es:ecx+100],0            ;T=0
。。。

其中TSS的内存分布:
在这里插入图片描述
T位为1,在多任务的环境中,每次切换到该任务时,将引发一个调试异常中断,我们将此位置0

10、TSS描述符的格式和创建

接上一节,本节在GDT中登记TSS描述符。

。。。
	;在GDT中登记TSS描述符
	mov eax,[es:esi+0x14]              ;TSS的起始线性地址
	movzx ebx,word [es:esi+0x12]       ;段长度(界限)扩展置32位
	mov ecx,0x00008900                 ;TSS描述符,特权级0
	call sys_routine_seg_sel:make_seg_descriptor
	call sys_routine_seg_sel:set_up_gdt_descriptor
	mov [es:esi+0x18],cx               ;登记TSS选择子到TCB
。。。

TSS描述符格式:
在这里插入图片描述

  • S = 0:表示系统描述符
  • TYPE = 10B1B(Busy),此位不固定,由处理器根据任务的状态实时修改,创建时清零。

其中这条TSS描述符参数:
在这里插入图片描述

  • S = 0:表示系统描述符
  • TYPE = 0000:表示此时不忙
  • G = 0:表示段界限以字节为单位
  • P = 1:表示段存在

11、用带参数的RET指令返回调用者

接上一节,本节从例程load_relocate_program返回调用者,在返回之前,栈状态:
在这里插入图片描述
在执行下列出栈指令之后:

...
	pop es		;恢复到调用此过程前的es段 
	pop ds		;恢复到调用此过程前的ds段
	
	popad
...

栈的状态为:
在这里插入图片描述
接着执行返回指令:

...
	ret 8		;丢弃调用本过程前压入的参数
				;因为之前压栈时有指令扩展操作
				;出栈时减掉,使得栈达到平衡
...

栈的状态:
在这里插入图片描述
其中ret imm、retf imm指令:
在这里插入图片描述
到目前为止内存布局:
在这里插入图片描述
在这里插入图片描述

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

接上一节,接下来从load_relocate_program返回,之后要转到任务的私有部分(用户程序)执行。

在多任务系统中,有多个任务,多个TSSLDT
在这里插入图片描述
8086使用TRLDTR存储TSSLDT的基地址和界限。
在这里插入图片描述
TRLDTR结构:
在这里插入图片描述
其中LDT选择器、TSS选择器是16位的。

TRLDTR可以使用ltrlldt指令加载:
在这里插入图片描述
LDTR加载过程:
在这里插入图片描述

  • lldt r/m16指令执行时,将选择子送入LDTRLDT选择器中;
  • 处理器从GDT中取出LDT描述符;
  • LDT描述符中的LDT线性基地址送入LDT描述符高速缓存器32位线性基地址部分;
  • 之后LDTR就指向LDT

TR加载过程:
在这里插入图片描述

  • ltr r/m16指令执行时,将选择子送入TRTSS选择器中;
  • 处理器从GDT中取出TSS描述符;
  • TR描述符中的TSS线性基地址送入TR描述符高速缓存器32位线性基地址部分;
  • 之后TR就指向TSS

接下来程序就条条转执行用户程序,并从用户程序返回。

13、在虚拟机上验证任务的执行

写数据:
在这里插入图片描述
在这里插入图片描述
Virtual Box虚拟机:
在这里插入图片描述
Bochs虚拟机:
modbp命令设置断点,将断点设置在处理器工作模式改变的地方,如实模式到保护模式。程序一开始没有使用modbp设置断点,因为程序复位之后BIOS会多次进入保护模式进行测试。

进入内核:
在这里插入图片描述
加载TSS处:
在这里插入图片描述
此时sreg命令查看寄存器状态:
在这里插入图片描述
GDT内容:
在这里插入图片描述
查看GDT中5号(从0开始)描述符:
在这里插入图片描述
LDT内容:
在这里插入图片描述
查看LDT中2号(从0开始)描述符:
在这里插入图片描述
TSS信息:
在这里插入图片描述
运行结果:
在这里插入图片描述
上一节:26、用户程序编程接口及其实现
下一节:28、特权级和特权级保护

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值