注:本文档为《从0学x86操作系统》课程配套的学习文档,提供相应的辅助学习资料和答疑勘误。 有关该课程的信息,请点击这里访问:https://study.163.com/provider/1017884735/index.htm 在阅读本文档时,如有疑问和建议,欢迎在下方留言或者直接联系我。

本小节解答为什么任务状态段TSS能够完整的描述一个程序运行的状态,以及用代码实现如何初始化TSS。

主要内容

视频中重点介绍了操作系统在多个程序之间切换运行的方法:保存前一程序的运行状态到其TSS中,再从下一程序的TSS中提取运行状态恢复到相应的位置。通过这样反复地保存-恢复-保存…恢复,就能实现一颗CPU内核,能够在多个程序间执行相应的代码,从而使得每个程序看起来是在独占CPU运行。
image.png
任务状态段的内容比较多且复杂,具体每个字段的含义见下方的【参考资料】。我们在本节课程中主要结合代码的编写进行讲解。
image.png
该结构大致可以分为以下几部分,目前只需要关注前4部分。虚拟内存CR3将在后续讲内存管理的章节关注,其它部分,本课时未涉及。
image.png
通过TSS结构,我们就能够知道一个程序当前的运行状态。在进行不同程序运行的切换时,需要将前一个程序完整的运行状态全部保存到TSS,这样当后续该程序需要再次运行时,再将该状态进行从TSS中恢复,从而就像能完整地继续从上次切换的位置继续往下运行,好像什么都没发生一样
具体来说,一个程序在运行起来后,首先会为程序代码(.text)、使用到的数据(.data .bss,全局变量、局部变量等)分配存储空间,即如我们在之前课时中看到的,loader将kernel加载到内存中运行时,需要将相应的代码和数据从文件中拷贝到特定的内存位置。这些内存会区域将在程序运行起来后被程序独自占有,不会被其它程序运行修改,所以这些内存中的当前内容虽然体现了当前程序的执行状态,但是并不需要保存。
而程序运行时在访问这些内存时,需要使用到CS/SS/ES/DS/FS/GS这些段寄存器,而这些寄存器在CPU中只有一份,是被多个程序所共享的。所以,为了在程序恢复运行时,这些段寄存器要和之前相同,TSS中要有相应的字段,来保存该程序使用了哪些段。
image.png
此外,程序在运行时,还会使用到各种通用寄存器进行运算、数据访问,因此这些寄存器也体现了当前程序的运行状态,因此也需要保存起来。
EFLAGS寄存器中有相应的标志位,其中的值受之前执行指令的影响,同时后面的条件跳转指令会依赖这些标志,因此也需要保存;否则,当程序恢复运行时,这些标志位没有恢复为原来的值,将导致接下来的跳转指令跳转到错误的位置。
EIP寄存器指定了当前执行的指令地址,显然这个值需要在切换之前保存起来,以便于当程序恢复运行时,继续从之前的位置往下运行。
image.png
综上所述,目前只需要在切换时保存好当前的段寄存器、通用寄存器、EIP、EFLAGS中的内容到当前程序的TSS中,然后再加载下一个将要运行程序的TSS中段寄存器、通用寄存器、EIP、EFLAGS值进行恢复。

参考资料

  • TSS相关信息,见课程附带的intel编程文档卷3 第252页:Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B, 3C & 3D): System Programming Guide