在我们考虑是否涉及“上下文切换”之前,我们应该澄清一下它的意思,或者更确切地说是它可能的含义。
当我们谈论操作系统内核和进程时,我们可以在多个方面进行“切换”:
1. 权限级别/环/模式
2. CPU寄存器集
3. 虚拟内存区域
通常不止一个发生。模式切换是一种非常频繁的情况,主管调用/异常或硬件中断(例如用于调度的计时器或外部设备)必然会触发这种情况。在几乎所有的情况下,我们需要获得更高的权限以实际“做些什么”。
当执行发生某种形式的异步中断时,需要存储所有CPU的寄存器。这对于主管调用并不一定需要,因为我们可以定义来电方和被叫方之间的呼叫约定,哪个寄存器负责在执行指令之前备份。这在硬件中断中根本不可能,因为我们不知道它们何时触发:进程如何能够预见自己将在接下来的t纳秒内被中断并必须备份所有已使用的寄存器呢?
在虚拟内存区域之间切换是必要的,例如在进程之间切换(每个进程都有自己的地址空间)或在进程和内核地址空间之间切换(内核也是如此)。但是,在主管调用中通常不需要这样做,因为它们通常被映射到进程的地址空间中,并且仅以更高的特权级别(内核模式)执行而不“离开进程”(进程的地址空间)。
尽管特权级别之间的切换相对便宜(性能方面),但完整的CPU寄存器集的切换已经相当昂贵。然而,卸载整个页表、刷新TLB等是最昂贵的操作,我们尽可能地把它降到最低。这也是线程通常比进程更具性能优势的原因:在它们之间切换只需要在CPU寄存器之间进行切换(并且根据实现的不同,可能需要一个简短的模式切换),而不需要在地址空间之间切换!
当我们阅读关于“上下文切换”的文献时,不幸的是并不总是清楚到底是指这三个中的哪一个或哪种组合实际上被称为“切换上下文”。从我所了解的情况来看,这个术语来自一个非常简化的机器模型,它没有区分中断、异常/主管调用,也没有区分切换CPU寄存器或地址空间。在这个模型中,我们只看到“内核[模式/空间/...]”和“用户[模式/空间/...]”,任何在这两者之间的切换都被视为“上下文切换”。
幸运的是,您发布的情景不符合任何这些切换,因为它是在同一地址空间内进行的同步、非特权函数调用,因此不需要任何切换,只需要一个简单的调用约定即可保存那些需要保存的寄存器即可。
1 但正如@BenVoigt所指出的那样,这可能会带来安全问题。对于像gettimeofday()
这样的调用,这不是问题(参见这篇LWN.net文章),但在其他情况下,这可能会导致将机密数据泄露给用户进程。
2 一些微内核设计分派了一个进程,该进程实际上处理了监管员调用的实现,从而必须切换到该进程的地址空间。
void main(void){
,main()
函数只有两种有效的签名,它们分别是:int main( void )
和int main( int argc, char *argv[] )
。请注意,它们都具有返回类型为int
,而不是void
。 - user3629249main()
的两个签名。 - user3629249