Task State Segment
任务状态段 (TSS-Task State Segment) 是特定于IA-32和X86-64体系结构的二进制数据结构。 它保存有关任务的信息。 在 保护模式 中,TTS; 主要适用于 硬件任务切换,其中每个单独的 任务 都有自己的 TSS。 在软件多任务中使用时,通常也会使用一两个,因为它们允许在中断后进入“Ring 0”代码。 在长模式中,TSS具有独立的结构,用于在中断或权限级别更改后更改堆栈指针(Stack Pointer)。 你必须自己在多任务处理功能中更新 TTS,因为它显然不会自动保存寄存器。
保护模式
对于在硬件任务切换中的使用,TSS包含程序的状态,包括通用寄存器、段选择器、指令指针、EFLAGS寄存器、EFLAGS寄存器、指令指针、EFLAGS寄存器 它包含下面描述的某些其他字段。
| 0x3 | 0x2 | 0x1 | 0x0 | Offset |
|---|---|---|---|---|
| SSP | 0x68 | |||
| IOPB | Reserved | 0x64 | ||
| Reserved | LDTR | 0x60 | ||
| Reserved | GS | 0x5C | ||
| Reserved | FS | 0x58 | ||
| Reserved | DS | 0x54 | ||
| Reserved | SS | 0x50 | ||
| Reserved | CS | 0x4C | ||
| Reserved | ES | 0x48 | ||
| EDI | 0x44 | |||
| ESI | 0x40 | |||
| EBP | 0x3C | |||
| ESP | 0x38 | |||
| EBX | 0x34 | |||
| EDX | 0x30 | |||
| ECX | 0x2C | |||
| EAX | 0x28 | |||
| EFLAGS | 0x24 | |||
| EIP | 0x20 | |||
| CR3 | 0x1C | |||
| Reserved | SS2 | 0x18 | ||
| ESP2 | 0x14 | |||
| Reserved | SS1 | 0x10 | ||
| ESP1 | 0x0C | |||
| Reserved | SS0 | 0x08 | ||
| ESP0 | 0x04 | |||
| Reserved | LINK | 0x00 | ||
- LINK: 上一个任务链接字段。 包含前一任务的TSS的Segment Selector。
- SS0, SS1, SS2: 当权限级别从较低权限级别更改为较高权限级别时,用于加载堆栈的Segment Selectors。
- ESP0, ESP1, ESP2: Stack Pointers用于在权限级别从较低的权限级别更改为较高的权限级别时加载堆栈。
- IOPB: I/O映射基址字段。 包含从 TSS 的基地址到I/O Permission Bit Map的16位偏移量。
- SSP: Shadow Stack Pointer.
长模式
在 长模式中,TSS不存储有关任务执行状态的信息,而是用于存储中断堆栈表(Interrupt Stack Table)。
| 0x3 | 0x2 | 0x1 | 0x0 | Offset |
|---|---|---|---|---|
| IOPB | Reserved | 0x64 | ||
| Reserved | 0x60 | |||
| Reserved | 0x5C | |||
| IST7 (High) | 0x58 | |||
| IST7 (Low) | 0x54 | |||
| IST6 (High) | 0x50 | |||
| IST6 (Low) | 0x4C | |||
| IST5 (High) | 0x48 | |||
| IST5 (Low) | 0x44 | |||
| IST4 (High) | 0x40 | |||
| IST4 (Low) | 0x3C | |||
| IST3 (High) | 0x38 | |||
| IST3 (Low) | 0x34 | |||
| IST2 (High) | 0x30 | |||
| IST2 (Low) | 0x2C | |||
| IST1 (High) | 0x28 | |||
| IST1 (Low) | 0x24 | |||
| Reserved | 0x20 | |||
| Reserved | 0x1C | |||
| RSP2 (High) | 0x18 | |||
| RSP2 (Low) | 0x14 | |||
| RSP1 (High) | 0x10 | |||
| RSP1 (Low) | 0x0C | |||
| RSP0 (High) | 0x08 | |||
| RSP0 (Low) | 0x04 | |||
| Reserved | 0x00 | |||
- RSP0, RSP1, RSP2: 当特权级别从较低的特权级别更改为较高的特权级别时,用于加载堆栈的Stack Pointers(栈指针)。
- IST#: 中断堆栈表(Interrupt Stack Table)。 “中断描述符表-Interrupt Descriptor Table”中的一个条目的IST值不是0时,用于加载堆栈的Stack Pointers。
- IOPB: I/O映射基址字段。 包含从TSS的基地址到I/O Permission Bit Map的16位偏移量。
软件多任务中的TSS
对于执行可能想要通过中断进行系统调用的进程的每个CPU,需要一个TSS。 唯一有趣的字段是SS0和esp0。 每当发生系统调用时,CPU都会在其TSS中获取SS0和ESP0值,并将堆栈指针分配给它。 因此,需要为执行系统调用的进程设置一个或多个内核堆栈。 请注意,线程/进程的时间片可能在系统调用期间结束,将控制权传递给另一个线程/进程,该线程/进程也可以执行系统调用,最终在同一堆栈中。 解决方案是为每个线程/进程创建一个私有内核堆栈,并在任何任务切换时重新分配esp0,或者在系统调用期间禁用调度(另请参见[[Kernel Multitasking|内核多任务])。
设置TSS很简单。 需要 全局描述符表 中的条目 (另请参阅 GDT教程),将TSS的地址指定为 “base”,TSS的大小指定为 “limit”,0x89 (Present|Executable|Accessed) 作为 “access byte”,0x40 (Size-bit) 作为 “flags”。 在TSS本身中,要设置成员“SS0”、“ESP0”和“IOPB偏移量”:
- SS0获取内核数据段描述符(例如,如果GDT中的第三个条目描述了内核数据,则为0x10)
- ESP0获取堆栈指针在系统调用时应获得的值
- 如果你不打算进一步使用此io-bitmap,IOPB可能会获得 sizeof(TSS)值(即104)(根据mystran http://forum.osdev.org/viewtopic.php?t=13678)
TSS的实际加载必须在保护模式下进行,并且必须在GDT加载之后进行。 加载过程很简单,如下所示:
mov ax, 0x?? ;GDT中TSS的描述符(例如,如果GDT中的第六个条目描述了你的TSS,则为0x28)
ltr ax ;实际加载
另见
文章
论坛主题
外部链接
- Task State Segment on Wikipedia