1. 简介

一个任务由两部分组成 : 任务的执行空间和 TSS , 前者包括代码段 , 栈段 , 一个或多个数据段 ; 后者指明构成任务的执行空间的段 , 为任务的状态信息提供存储空间 .

当前执行的任务状态包括以下组成部分 :

  • 任务的当前执行空间 , 由段寄存器 ( CS , DS , SS , ES , FS , GS ) 的段选择器定义 .
  • 通用寄存器的状态 .
  • EFLAGS 的状态 .
  • EIP 寄存器的状态 .
  • CR3 寄存器的状态 .
  • 任务寄存器的状态 .
  • LDTR 寄存器的状态 .
  • IO 映射的基地址和 IO 映射 .
  • 特权级 0 , 1 , 2 的栈指针 .
  • 指向前一个执行程序的链接 .
  • 影子栈指针的状态 .

2. 任务管理数据结构

2.1. Task State Segment ( TSS )

TSS 用于保存恢复一个任务所需要的处理器状态信息 , 分成两类 : 动态域和静态域 .

当一个任务在切换时被挂起 , 处理器更新动态域 , 包括 :

  • 通用寄存器域
  • 段选择器域
  • EFLAGS 寄存器域
  • EIP 域
  • 先前任务链接域

处理器读取静态域 , 通常不会修改 , 这些域在任务创建时设置 , 包括 :

  • LDT 段选择器域
  • CR3 控制寄存器域
  • 特权级 0 , 1 和 2 的栈指针域
  • T 标志 ( 调试陷入 )
  • IO 映射基地址域
  • 影子栈指针

开启分页时 , 如果 TSS 的前 104 字节 ( 大小为 108 字节 ) 跨越了一个页的边界 , 处理器可能不会正确执行地址转化 . 进程切换时 , 处理器读取和写入每个 TSS 的 104 字节 ; 因此 , 开始访问 TSS 后 , 如果 104 字节的一部分物理不连续 , 处理器不会产生缺页异常 , 访问错误的信息 .

2.2. TSS 描述符

和其他段一样 , TSS 通过一个段描述符定义 . TSS 的描述符只能保存到 GDT , 不能保存到 LDT 或 IDT.

使用一个设置了 TI 标志的段选择器访问一个 TSS 会在 CALLJMP 期间产生 #GP 异常 , 在 IRET 时产生 #TS 异常 .

TSS 描述符的类型域中的 B 标志指明任务是否繁忙 , 繁忙的任务目前正在运行或者挂起 .


64 位模式不支持任务切换 , 但是 TSS 描述符依然存在 .

2.3. 任务寄存器

TR 保存当前任务 TSS 的段选择器和整个段描述符 , 这个信息拷贝自 GDT 中当前任务的 TSS 描述符 .

TR 包含可见部分和不可见部分 , 前者可以由软件读取和修改 , 后者由处理器维护 , 软件不可访问 . 可见部分的段选择器指向 GDT 中的一个 TSS 描述符 , 处理器使用不可见部分缓存 TSS 的段描述符 , 提高任务的执行效率 .

LTR 指令先加载指向 GDT 中 TSS 描述符的段选择器到 TR , 然后加载 TSS 描述符中的信息到不可见部分 .

2.4. 任务门描述符

任务门描述符提供了对于任务的间接受保护的引用 , 可以放到 GDT , LDT 或者 IDT . 任务门描述符的 TSS 段选择器域指向 GDT 中的一个 TSS 描述符 . 段选择器中的 RPL 没有使用 .

一个任务门描述符或者 TSS 描述符都可以用来访问一个任务 , 两个结构满足了下列需求 :

  • 一个任务只能有一个繁忙标志
    由于任务的繁忙标志保存在 TSS 描述符 , 每个任务只能有一个 TSS 描述符 . 但是可能有多个任务门引用同一个 TSS 描述符 .
  • 针对任务提供可选择的访问
    任务门可以保存在 LDT , 具有不同于 TSS 描述符的 DPL , 没有足够权限访问 TSS 描述符的程序可以通过具有更大 DPL 的任务门访问任务 .
  • 用独立的任务处理中断或者异常
    任务门可以位于 IDT , 通过处理函数任务响应中断或者异常 . 当一个中断或者异常向量指向一个任务门 , 处理器切换到指明的任务 .

3. 任务切换

任务切换有 4 种情况 :

  • 当前的任务执行 JMPCALL 指令 , 调用 GDT 中的 TSS 描述符 .
  • 当前的任务执行 JMPCALL 指令 , 调用 GDT 或者 LDT 中的任务门描述符 .
  • 中断或者异常向量指向 IDT 中的任务门描述符 .
  • EFLAGS 寄存器的 NT 标志设置时 , 当前任务执行 IRET .

处理器切换任务时执行一大堆操作 , 详见 sdm . 还会检查一大堆的异常条件 , 这些条件依次进行判断 .

4. 任务链接

TSS 的上一个任务链接域和 EFLAGS 寄存器的 NT 标志用于返回到上一个任务 . EFLAGS.NT = 1 表明当前执行的任务嵌入到另外一个任务的执行中 .

执行 CALL 指令时 , 或者中断和异常导致的任务切换时 , 处理器拷贝当前 TSS 的段选择器到新任务的 TSS 的上一个任务链接域 , 然后设置 EFLAGS.NT = 1 .
软件使用 IRET 挂起新任务时 , 处理器检查 EFLAGS.NT = 1 , 然后使用上一个任务链接域返回到前一个任务 .

JMP 指令造成的任务切换不会设置 NT 标志 , 也不会使用上一个任务链接域 .

一个 TSS 只允许保存一个任务的一个上下文信息 ; 因此 , 对于一个任务的递归调用会导致任务的当前状态丢失 , TSS 段描述符的繁忙标志用于阻止重入的任务切换导致的任务状态信息的丢失 . 处理器按照下列方式管理繁忙标志 :

  • 分派一个任务时 , 处理器设置新任务的繁忙标志 .
  • 如果任务切换时当前的任务被放到一个嵌套链中 , 当前任务的繁忙标志保持设置 .
  • 如果切换到新的任务时处理器检测到新任务的繁忙标志已经设置 , 产生 #GP 异常 . 如果任务切换由 IRET 指令触发 , 不会产生异常 .
  • 一个任务由于跳转到新的任务 , 或者由任务代码中的 IRET 指令终止时 , 处理器清除繁忙标志 .

5. 任务地址空间

任务的地址空间由任务可以访问的段组成 , 包括 TSS 中引用的代码 , 数据 , 栈 , 系统段和任务代码访问的任何其他段 .

TSS 中的 LDT 域可以用于给每一个任务分配自己的 LDT , 通过将和任务相关的所有段的描述符放到任务的 LDT 中 , 隔离任务的地址空间 .

多个任务可以使用相同的 LDT , 高效的通信或者相互控制 .

任务之间可以通过下列方式为数据段创建共享的逻辑到物理地址空间的映射 :

  • GDT 内的段描述符
  • 共享的 LDT
  • 映射到线性地址空间的相同地址的不同 LDT 中的段描述符

6. 64 位模式任务管理

64 位模式不支持保护模式的任务切换机制 , 任务管理和切换必须由软件执行 , 处理器在下列情形中产生 #GP :

  • 使用 JMP , CALL , INT n , INT3 , INTO , INT1 或中断跳转到一个 TSS 或者任务门 .
  • EFLAGS.NT = 1 时执行 IRET 指令 .

虽然不支持任务切换 , 64 位的 TSS 必须存在 , 保存一些和任务切换机制无关的重要信息 , 包括 :

  • RSPn
    特权级 0-2 的栈指针的 64 位 canonical 形式 .
  • ISTn
    中断栈表指针的 64 位 canonical 形式 .
  • IO 映射基地址
    相对于 64 位 TSS 基地址的 IO 权限比特映射的偏移 .

激活 IA-32e 模式后 , 操作系统必须创建至少一个 64 位 TSS , 通过 LTR 指令给 64 位程序和兼容模式程序加载一个指向 64 位 TSS 的指针到 TR 寄存器 .