[转载]使用 TSS selector 进行任务切换

指令:

  call 0x20:00000000   jmp 0x20:00000000

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

selector 0x20 是个 TSS descriptor 的 selector。
  TSS descriptor 与 segment descriptor 的格式一致,不同的是 type,在 x86 和 long mode 的 compatibility 模式下有: available/busy 16-TSS 和 available/busy 32-TSS。   

当成功将 TSS descriptor 加载到 TR 寄存器后,processor 将 available TSS 置为 busy TSS,在返回时将 busy TSS 置为 available TSS。
1、索引查找 TSS descriptor   

TSS selector 在 GDT 里索引 TSS descriptor,其方法和 segment / gate dscriptor 的查找一致。

2、TSS 的 limit & type check
  TSS segment 大小必须能容纳所有的 processor state,整个 TSS segment 的大小是 104 个字节,在加载 TSS descriptor 进入 TR 之前,processor 检查 TSS descriptor 中的 limit 必须大于或等于 67h(104 字节)。若否,则产生 #TS 异常。   processor 也检查 TSS descriptor 的 type 是否为 available 类型。若为 busy 类型,则产生 #GP 异常。

if (temp_descriptor.type == BUSY) {   goto do_#GP_exception; }
if (temp_descriptor.limit < 0x67) {   goto do_#TS_exception; }

3、权限的 check
  所需的权限是:CPL <= DPL && RPL <= DPL

if (RPL <= DPL && CPL <= DPL) {    /* 通过,允许访问 */ } else {    /* 失败,#GP 异常 */ }

4、保存 old-task 的 processor  state
  在执行加载 TSS descriptor 到 TR 之前,processor 将执行一项很重要的工作:保存 old-task 的执行环境(processor 的状态)在当前的 TSS segment 中。   当前的 TSS segment 就是 old-task 的 TSS segment,在没加载 new-task 的 TSS 进入 TR 之前。
执行环境包括: (1)GPRs (2)segment registers (3)Eflags (4)LDTR 和 CR3 (5)EIP

5、加载 TSS descriptor
  通过一系列的 processor 检查后,processor 加载 TSS descriptor 进入 TR 寄存器。

TR.selector = TSS_selector; TR.limit = temp_descriptor.limit; TR.base = temp_descriptor.base; TR.attribute = temp_descriptor;
TSS_descriptor.type = BUSY_32;
CR0.TS = 1;

TSS descriptor 加载到 TR 寄存器相应的域。 并且,processor 将 TSS descriptor 的 type 置为 BUSY 状态,防止再调用该 TSS descriptor CR0.TS 也被置为 1,防止在 task 切换时执行 x87 or SSE(media)指令。

6、加载 processor state 信息
  TSS descriptor 加载进 TR,processor 将加载新任务保存在 TSS 的 processor 状态信息。 包括: (1)GPRs 加载 (2)segment registers 加载 (3)Eflags 加载 (4)LDTR 和 CR3 加载

7、设置 EFLAGS.NT 和 TSS.link 域
  使用 call 指令调用 TSS selector,processor 将置 Eflags.NT 为 1,表示当前的 task 被嵌套调用。而使用 jmp 指令则不会置 Eflags.NT 为 1。

if (opcode == CALL) {           /* 使用 call 指令 */   Eflags.NT = 1;                 /* 置 NT 位为 1 */   TSS.link = old_TSS;         /* link 域为 old task 的 TSS selector */ }

  jmp 指令不支持 Task-Nesting 因此不改变 Eflags.NT 域,使用 call 指令还将当前 TSS 的 link 域设为 old-task 的 TSS selector,这样,当前使用 iret 指令返回时,将通过 old-task 的 TSS selector 返回到 old-task。

7、执行新 task
  同样,processor 加载新的 task EIP 进入 EIP 寄存器,执行新的 task 代码。新的 CS 被加载时,CPL 就是新的 CS.RPL。

8、返回 old-task
  new-task 执行完毕通过 ret 或 iret 指令返回到 old-task。
在 caller 中:

... ...   call 0x20:0x0000000 next: ... ...

使用 call TSS_selector 来切换到 new-task。
(1)使用 ret 指令返回   在 new-task 中使用 ret 指令返回和一般的 caller / callee 之间返回并无两样。程序将返回到 next 处。
(2)使用 iret 指令返回   使用 Eflags.NT 的目的是让 iret 来返回,当使用 iret 指令返回时,processor 将检查 Eflags.NT 是否为 1 ,若 Eflags.NT = 1 则表示当前 task 被嵌套。   程序将返回到 TSS.link 的 TSS selector 指向的 TSS descriptor,这个 TSS descriptor 就是 old-task 的 TSS descriptor,这里又进行另一次的任务切换到 old-task。

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

使用 ret 与 iret 的不同就是:用 ret 返回时,当前的 processor 还是当前的 state,使用 iret 返回时,将加载保存在 TSS 的 processor state。   也就是说,ret 返回 old-task 还是沿用 new-task 的 processor 状态。iret 返回到 old-task 使用的是原来保存在 old-task 的 TSS 的 processor 状态。
使用的 iret 的另一个功能:   当 Eflags.NT = 1 时:由于 iret 将返回到 TSS.link 的 TSS selector,进行一次任务切换。若这个 TSS selector 是刻意构造的别有用途的 TSS 时,可以进行一次别有用途的 task 切换。

7.1.3.4.1、 long mode 下的 TSS 切换情形
  在 long mode 下已经不支持使用 TSS 来切换 task ,包括 TSS selector 和 task gate。使用 call / jmp TSS_selector 将产生 #GP 异常。

时间: 2024-10-07 18:56:55

[转载]使用 TSS selector 进行任务切换的相关文章

[转载]使用 task gate 进行任务切换

指令: call 0x20:00000000 jmp 0x20:00000000 ----------------------------------- selector 0x20 是个 task gate 的 selector 这里使用 task gate 任务切换与使用 TSS selector 的情形基本一样. 值得注意的是,使用 task gate 在权限的 check 方面与 call gate 不同的是: (1)call-gate 的权限 check 中:CPL <= DPLg &

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

segment descriptors 构建保护模式下的最基本.最根本的执行环境.system descriptors 则构建保护模式下的核心组件: 1.TSS descriptor 提供硬件级的进程切换机制 2.LDT descriptor 供进程使用多个 descriptor 3.Gate descriptor 提供 processor 权限级别的切换机制. 5.7.1. TSS 提供的进程切换机制 TSS 是一段内存区域,存放进程相关的执行环境信息.初始化的 TSS 是由用户提供,进程切换

[转载]long mode 模式下 system/gate descriptor 的疑惑

1. 32 位的 system descriptor 与 64 位的 system descriptor (1)compatibility 模式下,LDT / TSS descriptor 还是原来的 32 位的 descriptor,与原来 x86 的 LDT / TSS 意义一致. (2)64 bit 模式下,LDT / TSS descriptor 扩展为 64 位的 descriptor. descriptor 的 type 被相应改烃.由原来的 32 bit-LDT 改为 64 bit

[转载]使用 int n 调用系统例程

IDT(Interrupt Descriptor Table)仅能存放 interrupt-gate.trap-gate 和 task-gate. 指令: int 0x80 ----------------------------------- 0x80 是 vector (中断向量号) 在 x86 下,gate-descriptor 是 8 个字节,所以:gate = IDTR.base + vector * 8,在 long mode 下,gate-descrptor 是 16 字节,所以:

Linux内核的进程切换(上)

硬件上下文概念 尽管每个进程都可以拥有属于自己的地址空间,但所有进程必须共享CPU寄存器.因此在,在恢复一个进程执行之前,内核必须确保每个寄存器装入了挂起进程时的值.而这些必须装入的寄存器中的数据就称为硬件上下文. 任务状态段 80X86体系结构: 包括一个特殊的段类型,叫任务状态段(TSS),作用是存放硬件上下文.段是x86的概念,在保护模式下,段选择符参与寻址,段选择符在段寄存器中,而tss段则在tr寄存器中.当进程切换的时候就将 intel的建议: 为每一个进程准备一个独立的tss段,进程

新浪微博客户端(43)-切换表情控件

DJEmotionKeyboard.m #import "DJEmotionKeyboard.h" #import "DJEmotionListView.h" #import "DJEmotionTabBar.h" #import "DJEmotion.h" #import "MJExtension.h" @interface DJEmotionKeyboard() <DJEmotionTabBarD

[linux服务器][bash]让切换目录更方便

本文转载:[linux服务器][bash]让切换目录更方便: 一,为何要使用这几个命令?   可能大家会有疑问,为何要使用这几个命令,   难道用cd不就可以切换目录了吗?   没错,使用cd就可以切换到需要访问的目录,   但是有时会是一个路径很长,层次很多的目录,进到此目录下后,这时我们不小心运行了 cd命令,   理所当然,我们回到了自己的home目录,这时如果想回去怎么办? 还有:因为工作的需要,我们需要不停在几个很深层的目录之间切换,不止一个,   那么即使有tab键帮忙,我们也会因为

任务切换

本文为<x86汇编语言:从实模式到保护模式> 第15章笔记 由两种基本的任务切换方式, 一种是协同式额, 从一个任务切换到另一个任务, 需要当前任务主动地请求暂时放弃执行权, 或者在通过调用门请求操作系统服务时, 由操作系统"趁机"将控制转移到另一个任务. 这种方式依赖于每个任务的"自律"性, 当一个任务失控时, 其他任务可能得不到执行的机会. 另一种是抢占式的, 在这种方式下, 可以安装一个定时器中断, 并在中断服务程序中实施任务切换. 硬件中断信号总

linux0.11改进之四 基于内核栈的进程切换

这是学习哈工大李治军在mooc课操作系统时做的实验记录.原实验报告在实验楼上.现转移到这里.备以后整理之用. 完整的实验代码见:实验楼代码 一.tss方式的进程切换 Linux0.11中默认使用的是硬件支持的tss切换,系统为每个进程分配一个tss结构用来存储进程的运行信息(上下文环境),然后通过CPU的一个长跳转指令ljmp来实现进程的切换,这种方式易于实现,但一者不便于管理多CPU进程,二者效率不佳,故此次实验要将系统中使用的tss切换方式修改为栈切换方式.而由于CPU管理方式的原因,tr寄