调用一个函数是否被认为是上下文切换?

4
当我像下面这样调用一个函数时:
void main(void){
    Function();
}

如果我在跳转到函数之前保存寄存器,这是否被视为上下文切换?


4
这个问题不应该被迁移,它是关于理论计算机科学讨论中术语定义的问题,而不是关于行为的问题。 - Ben Voigt
1
上下文切换是一个标准化的术语。它指的是当操作系统从一个进程切换到另一个进程时,仅仅如此。它与该进程内部无关。 - Lundin
@BenVoigt 完全同意,我认为这反映在给出的答案在计算机科学方面相当不准确... - ljrk
1
关于 void main(void){main() 函数只有两种有效的签名,它们分别是:int main( void )int main( int argc, char *argv[] )。请注意,它们都具有返回类型为 int,而不是 void - user3629249
@larkey,尽管如此,C语言标准仅指定了main()的两个签名。 - user3629249
显示剩余3条评论
3个回答

7
不,上下文切换发生在内核交换进程时。调用用户态函数会使您处于同一进程中,因此不会进行上下文切换。然而,调用内核函数需要内核的操作,因此它是与内核进行的上下文切换。

当调用函数时,有些寄存器保持不变,但其他寄存器则被复制到程序堆栈。 - nir shahar
1
当从用户模式切换到内核模式并返回时,例如执行系统调用时,也会发生这种情况。 - user207421
1
操作系统中的上下文通常指寄存器和程序堆栈,更广义地说,这也包括页表项。在调用另一个函数时,这些内容都不会与其他上下文交换。但是,在执行系统调用时,即使在线程之间切换时也会引入内核上下文进行上下文切换。 - Anirudh
线程是“迷你进程”,所以在这种情况下会发生上下文切换(尽管与调用函数无关)。 - nir shahar
@Anirudh 嗯,系统调用并不需要备份所有寄存器,你有一个像普通函数一样的调用约定。但是你需要切换模式,有时还需要切换页表项。 - ljrk
显示剩余3条评论

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

1 但正如@BenVoigt所指出的那样,这可能会带来安全问题。对于像gettimeofday()这样的调用,这不是问题(参见这篇LWN.net文章),但在其他情况下,这可能会导致将机密数据泄露给用户进程。

2 一些微内核设计分派了一个进程,该进程实际上处理了监管员调用的实现,从而必须切换到该进程的地址空间。


1
这并不适用于监督员调用,因为我们可以在调用者和被调用者之间定义一种调用约定,以注册谁负责在执行指令之前备份。如果不这样做,就可能会暴露受保护的数据类型攻击(例如Heartbleed)。监督员需要恢复其使用的任何寄存器,这些寄存器可能包含特权数据,并且不能依赖调用者来完成。通常为了安全起见,系统调用应该恢复任何已使用的寄存器。 - Ben Voigt
1
总的来说,这是一个更有用的答案,特别是对于计算机科学讨论和文献,比其他答案更有价值。 - Ben Voigt
@BenVoigt 谢谢,我重新表述了那部分内容,加入了关于安全性的提示! - ljrk

0

什么是上下文切换?

当我们进行多任务处理时,实际上是不断地从一个任务切换到另一个任务。这就是上下文切换。

这个术语起源于计算机科学。运行多线程进程的CPU在运行另一个线程时会暂时挂起给定线程的执行。

调用函数不会切换进程,除了内核进程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接