x86-4-任务(task)

4.1 任务:

CPU将一整段正在运行的代码称作任务,可以类比操作系统的线程。比如说:你在Windows写了个程序进行运行,这个程序的运行在操作系统层面上来说就是进程里的线程,在CPU层面上来说就是任务。

 

4.2 操作系统中的任务:

在操作系统中操作系统的内核可以管理用户程序的加载与卸载,内核为用户程序提供各种编程接口,通过接口来对用户程序进行服务,用户程序通过调用内核提供的各种例程(routime)来进行工作。

比如Windows操作系统的进程/线程程序是由操作系统的内核+用户程序代码来编写的。

而程序映射到CPU底层来执行就是task(任务),所以说任务也是由:内核代码+用户代码组合而成:

img

 

 

 

内核只有一个作为所有任务的公共部分,而每个用户程序作为任务的私有部分。

内核是一直运行的,当一个任务被关闭时,只会关闭任务的私有部分而不会关闭内核。

一个内核可以为多个用户程序服务,然后一起组合成一个任务:

img

 

 

 

当一个任务结束时,只是用户程序部分(私有部分)结束了,而内核部分还在继续(只要操作系统不关闭)。

比如:你的Windows操作系统是一直开着的,它可以给你电脑上的微信提供帮助,然后和微信一起组合成一个进程(映射到CPU上就是任务),但是操作系统也可以和别的程序一起组合成其他的任务。而你关闭微信时并不会关闭操作系统,只有当你关闭操作系统时才会关机。

 

4.3 LDT(local Description table)表

如果CPU将所有的程序都放在GDT表中那么管理起来会非常不方便,因为每个程序都有自己的段,而操作系统中肯定会有很多程序,所以CPU建议对每个任务的用户程序都建立一个类似GDT表的东西来管理该任务的用户程序(当然操作系统也是这样做的)。所以就有了LDT表,从名称上来看也可以看出端倪,local Description table,局部段描述符表。

和GDT 一样,LDT 也是用来存放描述符的。只不过LDT只属于某个任务,所以LDT可以有多个。

img

 

 

LDT表和GDT表一样,也有一个寄存器来专门存放它的内容,就是ldtg寄存器:

kd> r ldtr
ldtr=00000000

 

4.4 任务的内存地址空间:

img

 

 

公有部分保存在GDT表中,私有部分保存在LDT中,由于LDT和GDT的数据结构是一致的,所有私有部分和公有部分的大小是一样的。

一个任务的理论总大小:

  私有部分: GDT的下表有13为的索引,所以有2的13次方个段,一个段的范围是2的32次方,所有大小为2的45次方也就是32TB

  共有部分:和私有部分一样。

  总理论大小:64TB。

 

虽然理论值是64TB,但是x86CPU中只采用了4GB的虚拟内存空间提供给任务使用。虚拟内存的意思就是这段内存可以正常使用但是并不是实实在在在内存中的,是虚拟的,因为任务太多了,如果所有都直接保存在内存中肯定不够用。

每个任务的虚拟内存和真实的物理内存一样大,所以任务的内容可以直接映射到对应的虚拟地址里面,因为大小一样。

任务的虚拟内存地址被称为线性地址(虚拟内存地址空间也叫线性地址空间),因为看着就是完整的一条内存线:

img

 

因为每个任务是由全局部分和虚拟部分组成,所以这个4GB的虚拟内存空间也被分成两部分:

 

 

从4GB内存空间中分成两半,高地址0xFFFFFFFF~0x80000000属于任务的全局部分,低地址0x7FFFFFFF到0x0000000属于任务的私有部分。

所以在多任务系统中任务的内存分布应该是这样的:

 

 

所以对于所有的任务来说:有一个2G的共享虚拟内存地址空间和一个2G的有人虚拟地址空间。

 

4.4.1 虚拟内存段式管理:

任务首先直接映射到虚拟内存中,然后要调用任务的段的时候从虚拟内存中的逻辑地址来访问段表(gdt和LDT)来得到内存中的地址,但是不可能所有的内容都在内存中保存,所以

采用的硬盘来作为一个外部存储设备来帮忙,将任务的段临时存放在硬盘中,然后根据需求再将其置换到内存里,当不需要使用时再将其置换到硬盘里。

每个段的段描述符中有一个P位,用来标识该段是否有效,也就是是否在内存中的意思,如果使用该段检测到P为0就会产生异常,然后从硬盘中读取该段再置换到内存里。这样来执行一个任务。

 所以当只有段式管理时,通过逻辑地址就直接得到物理地址。

4.5 TSS(Task State Segment)任务状态段

当任务在切换时,必须保存切换前任务的执行环境,不然切换回来后怎么保证继续执行是按照之前写好的代码逻辑执行的呢,所以引入了TSS数据结构来专门保存任务的环境。

TSS也有一个寄存器来存放它的首地址,叫做tr寄存器:

kd> r tr
tr=00000028

TSS的数据结构:

img

 

 

 

4.6 多任务系统:

多任务系统,是指能够同时执行两个以上任务的系统,也就是即使前一个任务没有执行完,其他任务也可以开始执行。

在单核CPU的系统中,多个程序不可能真的同时执行,但,CPU可以在多个任务之间周期性地切换。一会执行这个一会执行那个。所有任务都处于走走停停的状态,快速的CPU再加上高效的CPU任务切换,从其它角度看来,每个任务都是在同时运行。

目前基本上都是多任务操作系统了,所以说操作系 统时为了方便不再添加多任务这个关键字了。

 

多任务系统的组成示意图:

img

 

 

 

4.7 任务的执行流程:

在操作系统中任务大多文件相关,针对任务可以将文件分为两类,可执行文件和其它文件,可执行文件中包含了代码段数据段、栈段的定义的段的实际内容,其他文件可以被加载到任务的数据段中进行处理。比如说:一个QQ可执行程序执行了起来,然后这个QQ呢可以访问其它文件,比如用QQ来传图片给别人这样子。

每个任务的私有部分都有自己独立的可执行文件和其它文件,而内核部分则是公有的。

任务的执行流程根据内存管理的不同而不同,在intel中有段式和段页式,这里先讲一种笼统的:

首先将可执行文件映射到任务的虚拟内存空间中,然后虚拟内存又映射到物理内存中,最后CPU再通过物理内存进行实际性的操作。(如果要用到其他文件就将其加载到数据段中进行处理就行了)。

 

 

 

4.7.1段式管理中的任务执行流程:

段式内存管理,将内存分为一段一段的,但它大致上也与前面我们说的流程差不多:

首先将可执行文件的内容按照段映射到虚拟内存中,然后再按照段映射到物理内存中。最后CPU再通过物理内存进行实际性的操作。(如果要用到其他文件就将其加载到数据段中进行处理就行了)。

 

 

 

小结:

本来是想在WinDbg中验证的,可惜很遗憾,Windows没有采用这一套处理,Windows更多的是在分页管理上的运用。

比如说在Windbg中查看LDT的值:

kd> r ldtr
ldtr=00000000
kd> dd ldtr
00000000 ???????? ???????? ???????? ????????
00000010 ???????? ???????? ???????? ????????
00000020 ???????? ???????? ???????? ????????
00000030 ???????? ???????? ???????? ????????
00000040 ???????? ???????? ???????? ????????
00000050 ???????? ???????? ???????? ????????
00000060 ???????? ???????? ???????? ????????
00000070 ???????? ???????? ???????? ????????

它是一个0值,所以这一套基本上对于现代操作系统是没用了,但是对于CPU来说是有用的。