Linux调度程序是否需要进行上下文切换?

15

关于 Linux 调度程序和其他类似内核系统调用,我有一个一般性问题。

Linux 调度程序是否被视为一个“进程”,每次对调度程序的调用都需要上下文切换,就像它只是另一个进程一样?

假设我们有一个时钟滴答声中断了当前正在运行的用户模式进程,现在我们必须调用调度程序。调用调度程序本身是否会引发上下文切换?调度程序是否有其自己的寄存器、U-Area 和其他必须在每次调用时恢复的内容?

这个问题也适用于许多其他系统调用。内核进程在上下文切换方面是否像常规进程一样,唯一的区别是它们拥有更多的权限和访问 CPU 的能力?

我之所以提出这个问题,是因为上下文切换的开销很大。调用调度程序本身引起上下文切换并恢复调度程序状态,之后调度程序再调用另一个进程运行,再次进行上下文切换,听起来很奇怪。


实际上有一个问题:“调度程序(调度器)是如何工作的?” 我从我的一位前辈那里得到了答案。 请查看我的评论:https://dev59.com/Z2gv5IYBdhLWcg3wHNLG#17901703 - Shekhar Kumar
3个回答

5
那是一个非常好的问题,答案是“是”,但硬件意识到操作系统和任务调度的概念就不一样了。
在硬件中,您会发现有些寄存器是限定于“监管者”模式的。不详细介绍内部CPU架构,有基本程序执行寄存器的副本分别属于“用户模式”和“监管者模式”,后者只能被操作系统本身访问(通过内核设置的控制寄存器中的标志,该标志指示内核或用户模式应用当前正在运行)。
因此,你说的“上下文切换”是指交换/重置用户模式寄存器(指令寄存器、堆栈指针寄存器等等),但不需要交换系统寄存器,因为它们与用户寄存器分开存储。
例如,x86用户模式堆栈是USP - A7,而监管者模式堆栈是SSP - A7。因此,内核本身(包含任务调度程序)将使用监管者模式堆栈和其他监管者模式寄存器来运行自身,在其运行时将监管者模式标志设置为1,然后在用户模式硬件上执行上下文切换以在应用程序之间进行切换,并将监管者模式标志设置为0。
但是,在操作系统和任务调度的概念之前,如果您想要实现多任务系统,则必须使用您在问题中概述的基本概念:使用硬件中断每x个周期调用任务调度程序,然后将应用程序替换为任务调度程序,然后切换到新的应用程序。但在大多数情况下,定时器中断将是您实际的任务调度程序,并且它将被大量优化以使其更少上下文切换,而更像一个简单的中断处理程序例程。

1
以下是对调度程序调用过程的简单描述:
  1. 当前具有上下文的程序正在处理器上运行。寄存器、程序计数器、标志、堆栈基址等都适用于该程序;除了操作系统本地的“保留寄存器”之类的可能例外,该程序不知道任何有关调度程序的信息。
  2. 触发调度程序函数的定时中断。此时唯一发生的事情(在香草架构的情况下)是程序计数器立即跳转到BIOS中断中列出的PC地址。这开始执行调度程序的“分派”子程序;其他所有内容都不受影响,因此调度程序可以看到先前执行的程序的寄存器、堆栈等。
  3. 调度程序(像所有程序一样)具有一组操作当前寄存器集的指令。这些指令是以这样的方式编写的,它们知道先前执行的应用程序已经留下了所有状态。调度程序中的前几条指令将在某个地方将此状态存储在内存中。
  4. 调度程序确定下一个应该使用CPU的程序,获取其所有先前存储的状态,并将其填充到寄存器中。
  5. 调度程序跳转到任务中列出的适当PC计数器,该任务现在在CPU上具有完整的上下文。
总的来说,为了简化问题,调度程序不需要寄存器,它只需要将当前CPU状态写入预定的内存位置,从预定的内存位置加载另一个进程的CPU状态,并跳转到该进程上次运行的位置。

关于第二点。假设程序正在 CPU 的第 3 环上运行。之后我们跳转到计时器中断处理程序 - 调度程序,正如你所称。这是否意味着此时 CPU 被切换到第 0 环?如果是这样,那不就已经发生了上下文切换吗? - Alex

1

实际上,您可以查看kernel/sched.cschedule()函数的代码。它写得非常好,应该能回答您大部分的问题。

但归根结底,Linux调度程序是通过调用schedule()来调用的,它使用其调用者的上下文执行任务。因此,没有专门的“调度程序”进程。这实际上会使事情更加困难-如果调度程序是一个进程,它也必须调度自己!

当显式调用schedule()时,它只是将调用线程A的上下文与所选可运行线程B的上下文进行切换,以便它将返回到B(通过恢复寄存器值和堆栈指针,schedule()的返回地址将变为B的地址而不是A的地址)。


schedule() 调用 context_switch()。 - Saurabh

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