LDT:LocalDescriptorTableTSS:TaskStatusSegmentTCB:TaskControlBlockx86系统中的保护模式,为系统的安全性提供了很大的保障,但是在我们之前的文章中,权限级别的概念一直被淡化。比如:保护模式下的段选择符,我们一直把它看成是一个段描述符的“索引号”,用来在GDT(GlobalDescriptorDescriptorTable)中查找一个段描述符,例如:图中:代码段寄存器中的索引号为4,GDT中的每一项占用4个字节,所以在偏移量16处,找到代码段的描述符,然后从描述符中找到代码的起始地址和长度段的边界。数据段和堆栈段的操作过程也是如此。从现在开始,我们要让用户程序有自己的私有描述符表LDT(LocalDescriptorTable),有自己的特权级(不能让用户程序像操作一样工作在很高的0特权级)系统)。因此,我们需要更正之前的错误:在段寄存器中,不仅有段的索引号,还有另外两个属性:TI和RPL,如下图所示:TI标志:表示是哪个表(GDT或LDT)寻找描述符;TI=0:在GDT中寻找描述符;TI=1:寻找LDT中的描述符;RPL(RequestPrivilegeLevel)标志:表示请求者要给段寄存器(即一段代码)赋值,其特权级;此时,继续引用段寄存器的内容作为段索引是不合适的,一般称为:selector。LDT:LocalDescriptorTable在上一篇文章中,操作系统将应用程序从硬盘读入内存后,为应用程序创建了三个段描述符,并将这三个段描述符放在了GDT表中,这是不合理。首先,在多任务系统中,应用程序的数量是不确定的,应用程序的执行都会结束。如果所有的应用程序段描述符都放在GDT中,操作系统管理这些数据就太复杂了。其次,在引入特权级时,如果应用程序的段描述符放在GDT中,则意味着应用程序需要有访问GDT的权限,而x86系统中只有一个GDT(所以称为全局描述表)。由操作系统访问。因此,操作系统需要为每个应用程序申请一个单独的空间,作为程序自己的段描述表,称为:LDT(LocalDescriptionTable)。例如:现在系统中有2个用户程序:APP1和APP2。操作系统加载每一个应用程序时,都会在应用程序自身的内存空间中申请一块块作为LDT:为什么是“应用程序本身的内存空间”?因为每个应用程序都有独占的4G虚拟内存空间。在LDT中,存储了当前应用程序的段描述符信息,如代码段、数据段、堆栈段等。LDT占用的空间也是内存的一部分,并且有起始地址和长度限制,所以也需要为它创建一个段描述符,这个描述符放在GDT中。在Linux应用层,我们会严格区分进程和线程,但在系统底层,这种区分的界限已经模糊,更笼统的称之为任务。按照刚才的假设,系统中有2个用户程序,那么处理器怎么知道:当前是哪个应用程序在执行LDT中的代码呢?正如处理器中有一个寄存器GDTR,存放GDT的起始地址和长度一样,处理器中也有一个寄存器LDTR,存放当前正在执行的应用程序的LDT起始地址和长度:高位所有应用程序的虚拟内存的结束地址部分都映射到操作系统的内存空间,根据在Linux中,3G~4G的空间是操作系统使用的。图中绿色部分代表操作系统空间(1G)。在分页机制下,它们都映射到同一个物理内存页(蓝色虚线箭头)。当操作系统切换到应用程序2时,处理器中的LDTR会被赋予应用程序2的LDT的线性地址和长度信息。GDTR中的内容不变,因为每个应用程序中的GDT都是从操作系统“继承”的系统,起始地址和长度相同。TSS:TaskStatusSegment顾名思义,任务状态段用于存储和恢复任务状态信息。您经常听到一个术语:任务上下文。所谓上下文,就是反映一个任务执行时的环境信息,主要是处理器中各个寄存器的内容,也就是下图中的寄存器:这张图反映了一个任务上下文的所有寄存器信息。在任务被调度器终止之前,需要保存这些寄存器中的值,相当于拍了一张快照。后面任务恢复时,将快照中保存的信息原样赋值给图中所有寄存器,称为恢复任务上下文,任务将从上次暂停的地方继续执行(因为指令指针寄存器EIP被恢复)。TSS和LDT一样,也是操作系统为应用程序分配的内存空间,但是这个空间位于操作系统的影响范围内,只能由操作系统操作。TSS也有一个起始地址和一个长度界限,为此需要在GDT中创建一个段描述符。与LDT类似,处理器中也有一个寄存器TR,用于指向当前正在执行的任务的TSS。进行任务切换时:首先将处理器中寄存器的内容存入TR寄存器指向的TSS段(待停止的任务);然后,将新任务的TSS段内容复制到处理设备的各个寄存器中,并将TSS地址赋值给TR寄存器;TCB:TaskControlBlock任务控制块,可以说是系统中用来管理任务的最重要的数据结构,操作系统用来管理任务的所有信息都可以放在这里。看看Linux2.6内核代码中的结构:structtask_struct{...},就知道TCB有多复杂,有些书上也叫PCB(ProcessControlBlock,过程控制块)。在这个结构体中,一些常用的信息包括:程序的加载地址;任务的优先级;任务的当前状态;任务打开的一些资源:网络、文件设备等待;..需要注意的是,上面的LDT和TSS是在x86处理器中设计的运行机制,是处理器需要的。TCB不是处理器需要的,它是由操作系统的实现者构建的,因此可以根据自己的需要进行设计。每个应用程序都需要一个TCP结构,所有的TCB结构可以组成一个链表,便于操作系统管理。例如:当发生任务切换时,可以沿着链表头部一次扫描链表上的每个TCB节点。如果发现一个当前正在执行(即将被中止)的任务,则将该任务的状态标记为挂起,并移至链表尾部,然后将第一个处于就绪状态的任务放在最前面链表的加载到处理中以在设备中执行。当然,Linux系统中的处理过程要复杂一些。它根据优先级将每个任务放入不同的等待队列,然后使用哈希桶算法找到任务。Endx86处理器中的这三个概念对于理解任务切换非常重要。写到这里,总觉得上面的文字描述还是有些朦胧,可能还需要进一步了解上下文。先这样吧,以后想到更好的描述再分享给大家,谢谢!本文转载自微信公众号《IOT物联网小镇》【编辑推荐】抄袭不翻车:抵制千万级流量大规模分发系统架构设计五款开源游戏化工具七大趋势三冷2021年数字化转型趋势新Windows11预览版22449推送:开机动画怎么了?游戏玩家大规模退居Windows7:Windows10暴跌
