夜间模式暗黑模式
字体
阴影
滤镜
圆角
主题色
PCB与 TSS

前言

这学期在上操作系统的课程,在进程相关的部分接触到了context-switch这个概念。context-switch很好理解,但是这个过程是由谁来执行的呢?当我暗自得意自以为得知正确答案(TSS)的时候,被老师光速打脸(PCB)。当我去Intel开发者手册再次确认之后与dalao讨论,结果再次被打脸决定好好搞清楚这个问题(还是tclOrz)。

TSS是什么

我最早接触到相关的概念是在Linux 内核完全剖析这本书中,书中内核的运行环境是Intel 8086处理器,因此存在大量的相关硬件知识,其中就有TSS,Task Register,Task-gate Descriptor等等。其实就刚刚的问题回答TSS并不算准确,因为TSS(Task State Segment 任务状态段)只是一种数据结构而非保存/加载上下文的过程。我所指的应该是由硬件实现的上下文切换

我们注意到上述硬件处理上下文切换的相关结构中并没有Context这个单词,而是全部使用Task。这两个概念显然是有区别的:Context是对于process/thread而言,而Task所指的则是一个更大的概念,是前者的一个超集:

A task is a unit of work that a processor can dispatch, execute, and suspend. It can be used to execute a program,
a task or process, an operating-system service utility, an interrupt or exception handler, or a kernel or executive utility.

其实从另一个角度可以理解二者的不同:Context是对于OS而言;而Task则是面向CPU的概念,较前者有更进一步的抽象。

下面我们就从Task的范畴来粗略认识一下由硬件实现的任务切换。由于相关知识较为繁杂,这里仅对与Task Switch直接相关的部分进行介绍。注意:以下内容为IA-32体系结构。

如上图所示,一个Task(这里翻译为任务)由两部分组成:TSS与任务执行空间。其中任务执行空间包括了CS,SS,以及一个或多个DS,如果OS使用CPU的特权级保护机制,则为每个特权级都提供一个SS。TSS指定了构成任务执行空间的各个段,并且为任务状态信息提供了储存空间,其结构见下图。

处理器在下列情况时进行Task Switch:

  • 当前任务对GDT中的TSS描述符执行JMPCALL指令

  • 当前任务对GDT或LDT中的任务门描述符执行JMPCALL指令

  • 中断或异常向量指向IDT中的任务门描述符

  • EFLAGS中的NT标志位置位时当前任务执行IRET指令

Task-Switch的过程如下:

Task Switch
  1. Obtains the TSS segment selector for the new task as the operand of the JMP or CALL instruction, from a task gate, or from the previous task link field (for a task switch initiated with an IRET instruction).

  2. Checks that the current (old) task is allowed to switch to the new task. Data-access privilege rules apply to JMP and CALL instructions. The CPL of the current (old) task and the RPL of the segment selector for the new task must be less than or equal to the DPL of the TSS descriptor or task gate being referenced. Exceptions, interrupts (except for those identified in the next sentence), and the IRET and INT1 instructions are permitted to switch tasks regardless of the DPL of the destination task-gate or TSS descriptor. For interrupts generated by the INT n, INT3, and INTO instructions, the DPL is checked and a general-protection exception (#GP) results if it is less than the CPL.1

  3. Checks that the TSS descriptor of the new task is marked present and has a valid limit (greater than or equal to 67H). If the task switch was initiated by IRET and shadow stacks are enabled at the current CPL, then the SSP must be aligned to 8 bytes, else a #TS(current task TSS) fault is generated. If CR4.CET is 1, then the TSS must be a 32 bit TSS and the limit of the new task’s TSS must be greater than or equal to 107 bytes, else a #TS(new task TSS) fault is generated.

  4. Checks that the new task is available (call, jump, exception, or interrupt) or busy (IRET return).

  5. Checks that the current (old) TSS, new TSS, and all segment descriptors used in the task switch are paged into system memory.

  6. Saves the state of the current (old) task in the current task’s TSS. The processor finds the base address of the current TSS in the task register and then copies the states of the following registers into the current TSS: all the general-purpose registers, segment selectors from the segment registers, the temporarily saved image of the EFLAGS register, and the instruction pointer register (EIP).

  7. Loads the task register with the segment selector and descriptor for the new task’s TSS.

  8. If CET is enabled, the processor performs following shadow stack actions:

  9. The TSS state is loaded into the processor. This includes the LDTR register, the PDBR (control register CR3), the EFLAGS register, the EIP register, the general-purpose registers, and the segment selectors. A fault during the load of this state may corrupt architectural state. (If paging is not enabled, a PDBR value is read from the new task’s TSS, but it is not loaded into CR3.)

  10. If the task switch was initiated with a JMP or IRET instruction, the processor clears the busy (B) flag in the current (old) task’s TSS descriptor; if initiated with a CALL instruction, an exception, or an interrupt: the busy (B) flag is left set. (See Table 7-2.)

  11. If the task switch was initiated with an IRET instruction, the processor clears the NT flag in a temporarily saved image of the EFLAGS register; if initiated with a CALL or JMP instruction, an exception, or an interrupt, the NT flag is left unchanged in the saved EFLAGS image.

  12. If the task switch was initiated with a CALL instruction, an exception, or an interrupt, the processor will set the NT flag in the EFLAGS loaded from the new task. If initiated with an IRET instruction or JMP instruction, the NT flag will reflect the state of NT in the EFLAGS loaded from the new task (see Table 7-2).

  13. If the task switch was initiated with a CALL instruction, JMP instruction, an exception, or an interrupt, the processor sets the busy (B) flag in the new task’s TSS descriptor; if initiated with an IRET instruction, the busy (B) flag is left set.

  14. The descriptors associated with the segment selectors are loaded and qualified. Any errors associated with this loading and qualification occur in the context of the new task and may corrupt architectural state.

  15. If CET is enabled, the processor performs following shadow stack actions:

  16. Begins executing the new task. (To an exception handler, the first instruction of the new task appears not to have been executed.)

当然,使用TSS进行上下文切换存在其自身的局限性。它无法储存FPU/MMX/SSE的状态,因此对于上述相关的上下文切换无能为力。

PCB是什么

PCB(Process Control Block 进程控制块)用于记录进程的资源使用情况(包括软件资源和硬件资源)和运行态信息等。所有与进程相关的资源,比如进程ID、进程的页表、进程执行现场的寄存器值、进程各个段地址空间分布信息以及进程执行时的维护信息等,都被有组织地结构化到PCB内。

我们可以发现,PCB的作用并不仅限于进程切换中,而是为操作系统的进程管理部分提供了完整的决策信息。通过PCB内的进程信息,操作系统可以完成进程的调度、维护、切换等功能。

这里所说对进程的管理都是由软件来完成的,因此与PCB对应的上下文切换也就是 由软件实现的上下文切换

现实中的上下文切换

上文介绍了两种上下文切换的方式:软件/硬件实现的上下文切换。然而几乎所有的现代操作系统都选择在软件中进行上下文切换,这是由于TSS硬件切换的巨大缺点造成的:

  1. 相较于软件切换速度极慢
  2. 可移植性极差,在不同体系结构间甚至x86-64间移植困难

当然,使用软件切换并不意味着它完美无缺。相反,软件的上下文切换机制可能存在操作系统级的安全隐患。对此英特尔的一种处理方式为在软件切换中使用TSS。关于这部分的详细说明参加以下链接:

Linux为什么不通过TSS使用硬件上下文切换?

具有TSS的基于软件的上下文切换如何工作?

暂无评论

发送评论 编辑评论


				
上一篇
下一篇