当前位置: 代码迷 >> 综合 >> 操作系统概念详解----上下文切换
  详细解决方案

操作系统概念详解----上下文切换

热度:80   发布时间:2023-09-18 13:17:14.0

这是摘要:
进程上下文、中断上下文、用户态/内核态、操作系统、任务的全局部分和局部部分

《X86汇编语言:从实模式到保护模式》墙裂推荐
本文重点在于搞清楚三个问题:
1:什么是任务的局部空间,什么是任务的全局空间,以及什么是用户态/内核态切换
2:什么是用户任务(进程),什么是内核任务(进程),以及什么是进程上下文切换
3:当执行中断处理代码时,系统是位于哪个状态?是位于用户任务还是内核任务,还是位于用户任务的局部空间还是位于用户任务的全局空间以及什么是中断上下文切换
补充:什么是线程上下文切换
补充:特权级切换与栈切换:
补充:TSS和TCB是什么以及他们的区别

备注1:文中“任务”就是”进程”,“进程”就是任务;任务包括用户任务与内核任务,他两的区别就是一个特权级低,一个特权级高。
备注2:文中的上下文切换带来的代价均是指硬件层面所必须花费的最小代价,不包括软件层面(内核)的代价(最后面会解释)

准备知识:
1:段式内存管理与GDT(Global Descriptor Table)、LDT(Local Descriptor Table)和GDT(Global Descriptor Table Register)、LDTR((Local Descriptor Table Register)
摘要:一个程序被组织为多个段,有代码段,数据段,栈段
2:页式内存管理
任务的虚拟地址空间与页目录表以及PDBR(Page Directory Base Register)
摘要1:每个任务都有一个属于自己的页目录表,不同的任务具有不同的页目录表,切换页目录表就是切换了任务。可以简单认为一个页目录表唯一标志了一个任务。
摘要2:任务想要访问内存(例如指令/数据/栈),必须通过页部件把线性地址转换为物理地址之后才能访问。
摘要3:页部件根据页目录表把段部件产生的线性地址映射到实际的物理地址,即使是两个不同的页目录表,但只要两个页目录表中该线性地址映射的物理地址相同,那么同一个线性地址在不同任务内就可以访问同一个物理地址也就是数据共享/代码共享(此即共享内存实现原理)
3:TSS(任务状态段Task state segment)与TR(任务寄存器Task Register)
摘要1:TSS中保存了一些任务运行时所需要的数据(如不同特权级的栈)以及当发生任务切换时,TSS被用来保存当前任务的一个快照。
摘要2:TSS唯一标志了一个任务,TR寄存器中存放的是哪个TSS的选择子,那么当前就是位于哪个任务中。
总结:一个完整的任务包括代码段,数据段,栈段,以及一个页目录表和一个任务状态段

直击本质:
1:什么是任务的局部空间,什么是任务的全局空间,以及什么是用户态/内核态切换
一个用户任务包含代码/数据段栈段,不同的任务具有不同的代码/数据/栈段。一个任务需要访问OS(操作系统)所提供的各种例程/数据,也就是操作系统代码/数据所在的物理地址,就需要把操作系统所在的物理地址映射到用户任务的页目录表中去。不同用户任务执行不同的任务,所以就具有不同的代码/数据/栈段,这是每个用户任务私有的,所以这部分在不同的页目录表中映射到不同的代码/数据/栈段,但是操作系统的例程/数据是提供给所有用户任务使用的,所以每个用户任务的页目录表中都有一部分表项会映射到同一个物理地址空间,即OS。在实际实现时通常在不同任务的页目录表中统一把操作系统所在的的物理地址映射到相同的线性地址空间,也就是说每个任务的页目录表中有一部分是相同的,例如在32位系统中,linux就把内核统一映射到每个任务的地址空间的0xc0000000处(即0-4GB线性地址空间中的3GB~4GB处)。所以总的来说,我们就把每个任务私有的部分叫做任务的局部空间,把每个任务所共有的部分(即OS部分)叫做任务的全局空间;当执行任务的私有部分代码时便叫做用户态,当执行任务的全局部分代码时即使用操作系统提供的功能和数据时便叫做内核态;当一个任务通过系统调用从任务的私有部分转入全局部分时并没有发生页表切换,并不需要保存当前任务的所有状态,只需要简单地保存诸如指令指针,栈指针(发生特权级切换时会自动切换栈),标志寄存器的少数几个寄存器,所以说用户态/内核态的切换带来的代价相对来说比较小是指不需要保存大量的寄存器,但是注意状态切换仍然会带来缓冲区不命中的代价,这部分代价仍然是相当大的。

2:什么是用户任务,什么是内核任务以及什么是进程上下文切换。
不同的进程拥有不同的页目录表和TSS,当发生任务切换时,一是要保存当前任务的执行状态到TSS中,这需要保存大量寄存器,和用户态/内核态切换相比会带来更大的保存代价,二是也要切换页目录表,这也会带来缓冲区失效的代价,这个是切换页目录表,彻底切换了地址映射方式,所以缓冲区彻底失效(页目录表都不同了,新旧任务的线性地址映射到同一个物理地址而且这个物理地址对应的内存又恰好缓存在缓冲区中概率很小,除非映射了共享内存),前面说过的用户态和内核态之间的切换没有切换页表,也就是没有切换地址映射方式,当从内核态返回时,缓存中的用户态数据指令依旧是大概率可以被命中的,所以来说任务之间的切换也就是进程上下文之间的切换带来的代价会十分大。
上面说过,一个任务必然包括一份页表和一个TSS,这是固件要求的,但是在第一个问题中我们是直接把操作系统所在的物理内存直接映射到所有用户任务的页目录表中去,并没有单独为操作系统建立一份页目录表和一份TSS,所以说此时系统中只有用户任务,没有内核任务。所以说如果单独为操作系统建立一份页目录表和TSS,那么操作系统就作为一个独立的任务存在了,这个任务很特别,只有全局部分,没有私有部分,也就是把私有部分空出来了(当然实际也可以包含私有部分,当然,这是内核私有的,用户任务无法访问的,因为这部分代码/数据根本就没有映射到用户任务的页目录表中去,当然就无法访问了)。此时,系统中既有用户任务,也有内核任务,而且每个用户任务中都含有一份操作系统的“拷贝”,即内核任务中的操作系统部分是“原本”,用户任务中的操作系统部分是“副本”,原本和副本可以访问同样的代码和地址。
3:当执行中断处理代码时,系统是位于哪个状态?是位于用户任务还是内核任务,还是位于用户任务的局部空间还是位于用户任务的全局空间以及什么是中断上下文切换
这就很简单了:位于任务的全局部分!!!。当中断发生在任务的局部部分时,会切换到任务的全局部分的中断处理程序中去,也就是相当于用户态/内核态的切换。换句话说就是:中断上下文切换是特殊的用户态/内核态切换。发生中断时和用户态/内核态的切换时所执行的处理和进行的保存工作基本一致,除了某些异常(中断三大类中的异常)还需要压入错误码。毕竟中断处理程序也是属于当前任务的,只不过中断处理程序有点特殊罢了!!!!就相当于:我要住你家里和我认识你有关系吗???!!!
虽然中断是位于当前任务的全局部分,此时在linux系统中current仍然指向当前任务,但是!!current是无意义的,因为中断是随机发生的,与当前任务不一定有关系,所以此时访问用户任务是无意义的。注意,是无意义的而不是无法访问,实际上是能访问的,但是逻辑上不应该能访问。

4:额外附赠什么是线程上下文切换
更容易理解了:线程上下文切换既没有切换页目录表,也没有切换到全局部分,,缓存依旧大概率命中,所以线程上下文切换带来的代价和用户态/内核态相比保存代价差不多,都很小,但是线程上下文切换带来的缓存不命中代价比用户态/内核态切换可能要小一点。

硬件层面总代价=保存寄存器状态的代价 + 缓存不命中的代价
代价:线程上下文切换 < 用户态/内核态上下文切换 ≈中断上下文切换 << 进程上下文切换

补充:特权级切换与栈切换:
系统有特权级,这是固件支持的,不同的特权级可以访问不同的资源,执行不同的指令,如停机指令(hlt)就只能最高特权级执行。在从A特权级代码切换到B特权级代码时,会发生栈切换,简单来说就是TSS中对于每个特权级都保留了一个栈段选择子,切换到B特权级时,固件自动从TSS中取出对应特权级即B特权级的栈段选择子,然后把它放到栈段寄存器中就完成了栈切换,此时使用push/pop则是对新的B特权级栈进行操作了。从用户态切换到内核态,从用户态切换到中断处理程序都会发生栈切换,此时中断处理程序使用的是用户任务的高特权级栈!!!也就是说这个栈是用户任务用来在执行任务的全局部分时使用的,但是中断处理程序借用了这个栈!!!
TSS中对于每个特权级都保留了一个栈段选择子,这个是每个任务都不同的,也就是说内核任务和用户任务的全局部分在映射到同一片内存的情况下所使用的栈也是不同的。

补充:内核任务与用户任务唯一区别就是一个特权级低,一个特权级高。所以把某些功能从os里分离出去,单独为他们创建页目录表、TSS等,从而把这些功能实现为单独的任务,其中有的实现为高特权级任务(即内核进程),有的实现为低特权级任务(普通进程),换句话说就是某些书上说的:微内核把某某某功能实现为单独的任务,然后微内核的主要功能就是任务间的信息传递和分发即IPC

补充:TSS和TCB是什么以及他们的区别。
TSS是Task state segment即任务状态段,是处理器要求和支持的!!!如其名,是在发生任务切换时被处理器用来保存当前任务执行的机器状态的内存段。机器状态是指机器当前各寄存器的状态以及一些所必需的机器所需的信息。在x86 cpu中有一个TR寄存器,TR寄存器就是用来保存TSS段在内存中的线性地址,以便cpu自动寻址。TSS由操作系统为用户任务创建和初始化,但是TSS的维护和更新却是由硬件自动维护。
TCB是Tack control Block即任务控制块,是操作系统创建的,不是处理器要求的!!!是操作系统为了方便管理各个进程而创建的比如包括当前任务的执行状态(阻塞,挂起,可运行.etc)、当前任务打开的资源(文件、网络.etc)、当前任务等待的信号量等等。
在上下文(用户态/内核态、进程上下文、中断上下文)切换时TSS中的内容由处理器固件自动更新保存,TCB中的内容由操作系统在上下文切换前手动保存,所以上下文切换的总代价包括两个部分:硬件层面自动保存和硬件提供的缓冲区失效的代价+软件层面手动保存状态所带来的代价+软件层面实现的缓冲区代价以及其他许多代价比如重新打开某个文件、某个端口所新花费的时间等等,本文的代价专指硬件层面的代价。

  相关解决方案