【问题标题】:Is calling a function considered a context switching?调用函数是否被视为上下文切换?
【发布时间】:2020-09-21 12:25:18
【问题描述】:

当我调用如下函数时

void main(void){
    Function();
}

是否考虑到上下文切换,因为我在转到函数之前保存了寄存器?

【问题讨论】:

  • 这不应该被迁移,这是关于理论计算机科学讨论中使用的术语定义的问题,而不是关于行为的问题。
  • 这作为一个开放性问题更有帮助:如果我将函数调用视为上下文切换,那将意味着什么?通过这种方式思考可以收集到什么?
  • 上下文切换是一个标准化术语。它指的是操作系统何时将焦点从一个进程切换到另一个进程,仅此而已。它与该过程的内部无关。
  • @BenVoigt 完全同意,恕我直言,从 CS 的角度来看,给出的答案相当不准确......
  • 关于:void main(void){ main() 只有两个有效签名,它们是:int main( void )int main( int argc, char *argv[] )。请注意,它们的返回类型都是 int,而不是 void

标签: c++ c function context-switch


【解决方案1】:

不,上下文切换发生在内核交换进程时。调用 用户空间函数 会将您留在同一个进程中,因此不会是上下文切换。然而,调用内核函数需要来自内核的操作,因此它是到内核的上下文切换。

【讨论】:

  • 当一个函数被调用时,一些寄存器保持原样,而另一些被复制到程序堆栈
  • 在从用户模式切换到内核模式并返回时也会发生这种情况,例如执行系统调用时。
  • OS中的上下文通常是指寄存器和程序栈,广义上也包括页表项。当调用另一个函数时,这些都不会与另一个上下文交换。即使在线程之间切换时也会引入系统调用内核上下文。
  • 线程是“迷你进程”,所以在这种情况下会有上下文切换(尽管它与调用函数无关)
  • 交换完全是另一回事,甚至内核函数通常也不会离开进程地址空间。使用线程,您只需要在 CPU 寄存器之间切换,这可以被视为上下文切换。
【解决方案2】:

在我们问自己是否考虑这个“上下文切换”之前,我们应该弄清楚它的含义,或者说它可以意味着什么。

当我们谈论操作系统内核和进程时,我们可以在多个事物之间“切换”:

  1. 权限等级/戒指/模式
  2. CPU 寄存器集
  3. 虚拟内存区

通常,不只是其中一个单独发生。 模式切换是相当频繁的事情,主管调用/异常或硬件中断(例如用于调度的计时器或外部设备)必然会触发这些。在几乎所有场景中,我们都需要获得更高的权限才能真正“做某事”。

当我们有某种异步执行中断时,需要存储 所有 CPU 寄存器。这对于主管调用来说不是必要的,因为我们可以在调用者和被调用者之间定义一个调用约定,在执行指令之前注册谁负责备份1。这对于硬件中断来说是不可能的,因为我们不知道它们何时会触发:一个进​​程应该如何能够预见它将在接下来的 t 纳秒内被中断并且必须备份所有使用的注册?

例如,当我们需要在虚拟内存区域之间进行切换时在进程之间切换(每个进程都有自己的地址空间)或在进程和内核地址空间之间切换(内核也是如此)。但是,主管调用通常不需要它,因为它们通常映射到进程地址空间,因此只能以更高的权限(内核模式)执行,而不会“离开进程”(进程的地址空间)。2

虽然特权级别之间的切换相当便宜(性能方面),但切换出完整的 CPU 寄存器集已经相当昂贵。然而,卸载完整的页表、刷新 TLB 等是最昂贵的,我们会尽量减少这种情况。这也是线程(通常)比进程性能更高的原因:在它们之间切换只需要在 CPU 寄存器之间切换(并且,根据实现,短模式切换)而不是地址空间之间的切换!

当我们在文献中读到“上下文切换”时,不幸的是,并不总是清楚这三者中的哪一个或哪种组合实际上被称为“上下文切换”。据我所知,该术语来自一个非常简化的机器模型,它没有区分中断、异常/主管调用,也没有切换 CPU 寄存器或地址空间。在这个模型中,我们只看到“内核 [mode/space/...]”和“用户 [mode/space/...]”,它们之间的任何切换都被认为是“上下文切换”。

幸运的是,您发布的场景不适合这些开关中的任何,因为它是同一地址空间内的同步、非特权函数调用,因此不需要任何开关,而是一个简单的调用约定只保存那些需要保存的寄存器就足够了。


1 但这可能会产生安全隐患,正如@BenVoigt 正确指出的那样。对于gettimeofday()(参见this LWN.net article)之类的调用,这不是问题,但在其他情况下,这可能会导致机密数据泄露给用户进程。

2 一些微内核设计调度了一个进程,它实际上处理了主管调用的实现,因此需要切换到这个进程的地址空间。

【讨论】:

  • "这不是主管调用的情况,因为我们可以在调用者和被调用者之间定义一个调用约定,在执行指令之前注册谁负责备份。"这样做,现在你就暴露了受保护的数据类型攻击(参见例如 Heartbleed)。主管需要恢复它使用的任何可能包含特权数据的寄存器,而不是依赖调用者这样做。一般来说,为了安全起见,这意味着系统调用应该恢复所有以任何方式使用的内容
  • 一般来说,虽然这是一个比其他答案更有用的答案,特别是对于 CS 讨论和文学来说。
  • @BenVoigt 谢谢,我改写了那部分以添加点头。安全!
【解决方案3】:

上下文切换?

当我们同时处理多项任务时,我们真正要做的就是不断地从一项任务切换到另一项任务。这就是上下文切换。

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

调用函数不会切换除内核进程之外的进程

【讨论】:

    猜你喜欢
    • 2014-11-10
    • 2019-10-09
    • 2017-06-07
    • 2014-09-25
    • 1970-01-01
    • 2012-01-12
    • 2018-07-30
    • 2011-11-29
    • 1970-01-01
    相关资源
    最近更新 更多