[转载]x86 的 TSS 任务切换机制

segment descriptors 构建保护模式下的最基本、最根本的执行环境。system descriptors 则构建保护模式下的核心组件:

1、TSS descriptor 提供硬件级的进程切换机制 2、LDT descriptor 供进程使用多个 descriptor 3、Gate descriptor 提供 processor 权限级别的切换机制。

5.7.1、 TSS 提供的进程切换机制
TSS 是一段内存区域,存放进程相关的执行环境信息。初始化的 TSS 是由用户提供,进程切换时的保存信息由 processor 执行。
5.7.1.1、  三个元素构成 TSS 环境:
1、 TSS descriptor:这个 descriptor 属于 system descriptor 类型,它的 S (system)位是 0。
  下面列出 TSS descriptors 的类型值:
0001:  16-bit TSS 0

011: busy 16-bit TSS

1001:  32-bit TSS

1011: busy 32-bit TSS

---------------------------------------------------   

以上是 x86 下的 TSS descriptor 类型,分为 16 和 32 位 TSS,x64 的 long mode 下 32 位的 TSS descriptor 将变为 64 位 TSS descriptor。
情景提示:

关于 TSS 的 busy 与 available 状态:   1、 TSS descriptor 的类型指明是 busy 与 available 状态。   2、 TSS descriptor 的 busy 与 available 状态由 processor 去控制。即:由 processor 置为 busy 或 avaibable。 除了初始的 TSS descriptor 外。

  TSS 的 busy 状态主要用来支持任务的嵌套。TSS descriptor 为 busy 状态时是不可进入执行的。同时防止 TSS 进程切换机制出现递归现象。
2、 TSS selector 以及 TR(Task Register)寄存器
  TR 寄存器的结构与 segment registers 是完全一致的,即:由软件可见的 selector 部分与 processor 可见的隐藏部分(信息部分)构成。   TR.selector 与 CS.selector 中的 selector 意义是完全一样的。其 descriptor 的加载也是一样的。
  即: TR.selector 在 GDT / LDT 中索引查找到 TSS descriptor 后,该 TSS descriptor 将被加载到 TR 寄存的隐藏部分。当然在加载到 TR 寄存器之前,要进行检查通过了才可加载到 TR 寄存器。若加载 non-TSS descriptor 进入 TR 则会产生 #GP 异常。
3、 TSS 块(Task Status Segment)   

像 code segment 或 data segments 一样,最终的 TSS segment 由 TSS descriptor 来决定。 TSS descriptor 指出 TSS segment 的 base、limit 及 DPL 等信息。   T

SS segment 存放 eflags 寄存器、GPRs 寄存器及相关的权限级别的 stack pointer (ss & sp)、CR3 等等信息。

5.7.1.2、 TSS 机制的建立
  对于多任务 OS 来说,TSS segment 是必不可少的,系统至少需要一个 TSS segment,但是现在的 OS 系统不使用 TSS 机制来进行任务的切换。

情景提示:   TSS 存在的唯一理由是:需要提供 0 ~ 2 权限级别的 stack pointer,当发生 stack 切换时,必须使用 TSS 提供的相应的 stack pointer。   但是:若提供空的 TSS segment,或者可以考虑以直接传递 stack pointer 的方式实现 stack 切换,即便是这样设计 processor 要读取 TSS segment 这一工作是必不可少的。

下面的指令用来建立初始的 TSS segment:   

LTR word ptr [TSS_Selector]                /* 在 [TSS_selector] 提供 TSS selector */

或:

LTR ax                                               /* 在 ax 寄存器里提供 TSS selector  */
  ltr 指令使用提供的 selector 在 GDT / LDT 里索引查找到 TSS descriptor 后,加载到 TR 寄存器里。初始的 TSS descriptor 必须设为 available 状态,否则不能加载到 TR。processor 加载 TSS descriptor 后,将 TSS descriptor 置为 busy 状态。

5.7.1.3、  TSS 进程切换的过程
  当前进程要切换另一个进程时,可以使用 2 种 selector 进行:使用 TSS selector 以及 Task gate selector(任务门符)。
如:   

call 0x2b:0x00000000          /* 假设 0x2b 为 TSS selector */

call 0x3b:0x00000000          /* 假设 0x3b 为 Task-gate  selector */
  TSS 提供的硬件级进程切换机制较为复杂,大多数 OS 不使用 TSS 机制,是因为执行的效能太差了。上面的两条指令的 TSS 进程切换的过程如下:
1、使用 TSS selector  
  call 0x2b:0x00000000        /* 0x2b 为 TSS selector */
  这里使用 jmp 指令与 call 指令会有些差别,call 允许 TSS 进程切换的嵌套,jmp 不允许嵌套。
(1)processor 使用 TSS selector (0x2b) 在 GDT 索引查找第 5 个 descriptor
(2)processor 检查找到的 descriptor 类型是否是 TSS descriptor,不是的话将产生 #GP 异常。是否为 available TSS,若目标 TSS descriptor 是 busy 的话,同样将产生 #GP 异常。
(3)processor 进行另一项检查工作:权限的检查,CPL <= DPL 并且 selector.RPL <= DPL 即为通过,否则产生 #GP 异常。
(4)当前进程的执行环境被保存在当前进程的 TSS segment 中。

情景提示:   在这一步里,此时还没发生 TSS selector 切换,processor 把当前进程的环境信息保存在当前的 TSS segment 中。

(5)这里发生了 TSS selector 切换。新的 TSS selector 被加载到 TR.selector,而新的 TSS descriptor 也被加载到 TR 寄存的隐藏部分。

情景提示:   (1)这里,processor 还要将旧的 TSS selector 保存在当前的 TSS segment(新加载的 TSS)中的 link 域。这个 TSS segment 中的 link 其实就是 old TSS selector 域,用来进程返回时获得原来的 TSS selector 。从而实现任务的嵌套机制。   (2)processor 将当前 eflags 寄存器的 NT(Nest Task)标志位置为 1,表明当前的进程是嵌套内层。

(6)processor 从当前的 TSS segment 取出新进程的执行环境,包括:各个 selector registers(segment registers)、GPRs、stack pointer (ss & sp)、CR3 寄存器以及 eflags 寄存器等。   在这一步,在加载 selectors 进入 segment registers 之前,还必须经过相关的 selector & descriptor  的常规检查以及权限检查。通过之后才真正加载。否则同样产生 #GP 异常。

情景提示:   processor 还要做另一项工作,就是:将新进程的 TSS descriptor 置为 busy 状态。使得新进程不能重入。

(7)processor 从当前的 CS:RIP 继续往下执行,完成这个 TSS 进程的切换。这个 CS: RIP 就是新加载的新进程的 cs : rip
---------------------------------------------------------------------------------------   

从上面的过程可以看出,使用 TSS 进程切换机制异常复杂,导致进程切换的效能太差了。比使用 call gate 以及 trap gate 慢上好多。若当中发生权限的改变,还要发生 stack 切换。   

进程的返回同样复杂。同样需要这么多步骤,总结来说,主要的时间消耗发生在新旧进程的信息保存方面。

2、 使用 task gate selector      

另一种情况是使用 task gate selector 进行 TSS 进程切换,使用 task gate selector 除了可以 call/jmp 外,还可用在中断机制上,下面的两种情况:
  call 0x3b:0x00000000            /* 0x3b 为 task gate selector */

或:

int 0x3e                              /* 假设 0x3e 是 task gate 的向量 */
  这两种情形差不多,除了一些细微的差别外,不过是触发的机制不同而已。
  processor 在 GDT 索引查找到的是一个 task gate descriptor,这个 task gate descriptor 中指出了目标的 TSS selector 。processor 从 task gate descriptor 里加载 TSS selector,剩下的工作和使用 TSS selector  进行切换进程是一致。

  processor 访问 task gate descriptor 仅需对 task gate descriptor 作出权限检查:CPL <= DPL 并且 RPL <= DPL。在这里不需要作出对 TSS descriptor 的 DPL 权限进行检查。

  gate descriptor 机制提供了一层间接的访问层,主要用来控制权限的切换。
实际上:   

对于 call 0x2b:0x00000000 这条指令,processor 并不知道这个 selector 是 TSS selector 还是 task gate selector,在查找到 descriptor 后,processor 查看这个 descriptor 的 type 才能确定是 TSS selector 还是 task gate selector。

最后需注意:   (1)使用 jmp 指令进行转移,processor 不会将 eflags 中 NT 标志位置 1   (2)使用中断机制下的 TSS 进程切换,processor 将不作任务的权限检查动作。

5.7.1.4、 TSS 进程的返回
  从 TSS 机制切换的进程在执行完后使用 iret 指令返回原进程进,同样会发生新旧 TSS segment 的更新动作。
  进程通过使用 ret 返回时,processor 将不会从嵌套内层返回到的嵌套外层进程,也就是不会返回原进程。processor 对 ret 指令的处理,只会从 stack  pointer(ss : esp) 取返回地址。

对于使用进程使用了 iret 中断返回指令时:
(1)processor 将会检查当前的 eflags.NT 标志位是否为 1,也就是检查当前进程是否处于嵌套的内层。
(2)eflags.NT = 1,processor 将从当前 TSS segment 中的 link(old TSS selector)域中取出原来进程的 TSS selector。
(3)processor 将不作任何的权限检查,TSS selector 被加载到 TR.selector,TSS descriptor 同时被加载到 TR 的隐藏部分。
(4)processor 将清除当前的 eflags.NT 为 0。若是使用 ret 指令返回的,processor 是不会清 eflags.NT 为 0
(5)从 TSS segment 中加载新的进程执行环境,从新的 CS:EIP 处继续执行。 将原来的 TSS descriptor 重新置为 available 状态,使得可以再次进入。
-----------------------------------------------------------------------------
  由上可得,processor 遇到 ret 指令时,是不会对 eflags.NT 进行检查的。而使用 iret 指令,processor 将对 eflags.NT 进行检查。

时间: 2024-08-17 10:08:32

[转载]x86 的 TSS 任务切换机制的相关文章

通过简单的时间片轮转多道程序分析进程的启动和进程的切换机制

备注:秋风 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 1.配置好环境之后,使用qemu模拟器启动内核,运行效果如下: 2.实现时间片轮转多道程序调度的源码: mymain.c /*  *  linux/mykernel/mymain.c  *  *  Kernel internal my_start_kernel  *  *  Copyright (C) 2013  Men

uCGUI 按键窗口切换机制(更新篇)

在之前文章中,讲述了一个低内存使用量的的窗口切换机制.有人会问,低内存使用量是多低呢,我这里举个例子.我有一个项目中使用到本切换机制,128*64的单色屏,初步计算有105个窗口(后面还会增加),总内存使用量(包括任务栈)=105*3*4(窗口树) + 1024(公共buff) + 512(任务栈) + 1024*3(uCGUI动态内存) = 6k左右.从这个数字可以看出内存算是使用比较少的了.毕竟有100多个窗口,我在设计时也就只做了10多个窗口,这100多个窗口就是由10多个窗口大量复用组合

Spark系列(五)Master主备切换机制

Spark Master主备切换主要有两种机制,之中是基于文件系统,一种是基于Zookeeper.基于文件系统的主备切换机制需要在Active Master挂掉后手动切换到Standby Master上,而基于Zookeeper的主备切换机制可以实现自动切换Master. 切换流程图 流程说明: Standby Master模式 1. 使用持久化引擎读取持久化的storeApps.storeDrivers.storeWorkers,持久化引擎有FileSystemPersistenceEngin

WebCollector多代理切换机制

使用同一IP长期爬取网站容易被网站的反爬虫机制封杀IP.爬虫往往使用多代理的方法来应对反爬虫机制. 本教程利用WebCollector爬取大众点评,展示WebCollector的多代理切换机制,相关内容都在代码注释中. 教程中仅仅将网页保存在download文件夹中,如果需要抽取,请参考WebCollector其他教程. import cn.edu.hfut.dmic.webcollector.crawler.BreadthCrawler; import cn.edu.hfut.dmic.web

Keepalived中Master和Backup主备切换机制浅析

在keepalived的VRRP实例配置中会一般会设置Master和Backup来指定初始状态,但是这并不意味着此节点一直就是Master角色.控制节点角色的是Keepalived配置文件中的"priority"值和vrrp_script模块中设置的"weight"值.下面分别分情况对主备机切换机制作详细说明. 配置简介: 主机 IP 操作系统 软件 VIP 备注 nginx01 172.27.9.91 Centos7 keepalived nginx 172.27

[转载]x86控制转移及相关的权限检查

■ 数据访问时的权限check 一. 访问data segment时(ds.es.fs 及gs) 1. 程序指令要访问数据时,data segment selector 被加载进 data segment register(ds.es.fs 和 gs)前,处理器会进行一系列的权限检查,通过了才能被加载进入segment register.处理器分为两步进行检查: ★ CPL(当前程序运行的权限级别)与RPL(位于selector中的 RPL)作比较,并设置有效权限级别为低权限的一个. ★ 得出的

【转载】linux2.6内核initrd机制解析

题记 很久之前就分析过这部分内容,但是那个时候不够深入,姑且知道这么个东西存在,到底怎么用,来龙去脉咋回事就不知道了.前段时间工作上遇到了一个initrd的问题,没办法只能再去研究研究,还好,有点眉目,索性整理了一下. 网络上流传着很多关于ramdisk.initrd的各种版本的分析,我的这篇源于对他们的理解,非常感谢那些前辈的无私奉献,要不然我们这些晚辈学起东 西来该是多么艰难呀.在这里需要特别声明的是如果文中有引用了您的思想而没有给出参考文献,请您原谅我的疏忽.晚辈就是需要站在像您这种巨人的

(转载)JVM实现synchronized的底层机制

目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Lock孰优孰劣,只是介绍二者的实现原理. 数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令,大家可能会进一步追问:JVM底层又是如何实现synchronized的? 本文所指说的JVM是指Hotspot

[转载]x86 控制转移权限检查精要

一.       直接转移(far call及  far jmp) 直接转移通过执行一条 call 或 jmp指令,在段内转移不需selector近跳转,段间转移通过selector(不带gate或  tss)远跳转,CPL不改变. 权限检查的4个要素: ★ CPL:当前运行级别(也就是CS.CPL) ★ RPL:门符选择子(RPL for code descriptor) ★ DPL:门符DPL(DPL of code descriptor) ★ nonconforming/conformin