调度程序在哪里运行?

Ras*_*ing 5 scheduler cpu-architecture

刚刚完成了一本关于comp的书。架构,我发现自己并没有完全弄清楚调度程序在哪里运行。

我希望澄清的是调度程序在哪里运行 - 它是否有自己的核心分配来运行它而不是别的,或者“调度程序”实际上只是一个更模糊的算法,它在每个线程中实现执行 - 例如。在线程抢占时,是否运行 swithToFrom() 命令?

我不需要根据 windows x/linux x/mac os x 的细节,一般来说。

Rod*_*ney 6

不,调度程序不在它自己的核心中运行。事实上,多线程在多核 CPU 普及之前就已经很普遍了。

查看调度程序代码如何与线程代码交互的最佳方式是从一个简单的、协作的、单核示例开始。

假设thread A正在运行并thread B正在等待一个事件。thread A发布该事件,这会导致thread B变得可运行。事件逻辑必须调用调度程序,并且出于本示例的目的,我们假设它决定切换到thread B。此时,调用堆栈将如下所示:

thread_A_main()
post_event(...)
scheduler(...)
switch_threads(threadA, threadB) 
Run Code Online (Sandbox Code Playgroud)

switch_threads将 CPU 状态保存在堆栈上,保存thread A的堆栈指针,并使用 的堆栈指针的值加载 CPUthread B堆栈指针。然后它会从栈中加载剩余的 CPU 状态,这里的栈现在是栈 B。 此时,调用栈变成了

thread_B_main()
wait_on_event(...)
scheduler(...)
switch_threads(threadB, threadC)
Run Code Online (Sandbox Code Playgroud)

换句话说,线程 B 现在已经在它之前将控制权交给线程 C 时所处的状态唤醒。当switch_threads()返回时,它将控制权返回给thread B

堆栈指针的这些操作通常需要一些手工编码的汇编程序。

添加中断

Thread B正在运行并且发生定时器中断。调用堆栈现在是

thread_B_main()
foo()   //something thread B was up to
interrupt_shell
timer_isr()
Run Code Online (Sandbox Code Playgroud)

interrupt_shell是一个特殊的功能。它不被称为。它由硬件抢先调用。foo()没有调用interrupt_shell,所以当interrupt_shell返回控制权时foo(),它必须准确地恢复CPU状态。这与普通函数不同,普通函数根据调用约定返回离开 CPU 状态。由于interrupt_shell遵循与调用约定不同的规则,因此也必须用汇编程序编写。

的主要工作interrupt_shell是识别中断源并调用适当的中断服务程序 (ISR),在这种情况下为timer_isr(),然后将控制权返回给正在运行的线程。

添加抢占式线程切换

假设timer_isr()决定是时间片的时候了。线程 D 将获得一些 CPU 时间

thread_B_main()
foo()   //something thread B was up to
interrupt_shell
timer_isr()
scheduler()
Run Code Online (Sandbox Code Playgroud)

现在,此时scheduler()无法调用switch_threads(),因为我们处于中断上下文中。但是,它可以在不久之后被调用,通常是最后一件事interrupt_shell。这使thread B堆栈保存在此状态

thread_B_main()
foo()   //something thread B was up to
interrupt_shell
switch_threads(threadB, threadD)
Run Code Online (Sandbox Code Playgroud)

添加延迟服务例程

某些操作系统不允许您执行复杂的逻辑,例如从 ISR 内进行调度。一种解决方案是使用延迟服务例程(DSR),它以高于线程但低于中断的优先级运行。使用这些是为了在scheduler()仍然需要保护不被 DSR 抢占的同时,ISR 可以毫无问题地执行。这减少了内核必须屏蔽(关闭)中断以保持其逻辑一致的位置的数量。

我曾经将一些软件从具有 DSR 的操作系统移植到没有 DSR 的操作系统。对此的简单解决方案是创建一个运行优先级高于所有其他线程的“DSR 线程”。“DSR 线程”只是替换了其他操作系统使用的 DSR 调度程序。

添加陷阱

到目前为止,您可能已经在我给出的示例中观察到,我们从线程和中断上下文调用调度程序。有两种进出方式。它看起来有点奇怪,但确实有效。然而,继续前进,我们可能希望将我们的线程代码与我们的内核代码隔离开来,我们使用陷阱来做到这一点。这是用陷阱重做的事件发布

thread_A_main()
post_event(...)
user_space_scheduler(...)
trap()
interrupt_shell
kernel_space_scheduler(...)
switch_threads(threadA, threadB) 
Run Code Online (Sandbox Code Playgroud)

陷阱会导致中断或类似中断的事件。在 ARM CPU 上,它们被称为“软件中断”,这是一个很好的描述。

现在所有调用switch_threads()开始和结束都在中断上下文中,顺便说一下,这通常发生在特殊的 CPU 模式中。这是迈向特权分离的一步。

如您所见,日程安排不是一天建成的。你可以继续:

  • 添加内存映射器
  • 添加进程
  • 添加多个核心
  • 添加超线程
  • 添加虚拟化

阅读愉快!


Omi*_*SCI -1

通用调度程序分为三种类型:

作业调度程序也称为长期调度程序。

短期调度程序也称为 CPU 调度程序。

中期调度程序,主要用于交换作业,以便可以进行非阻塞调用。这通常是因为 I/O 作业不要太多或太少。

在一本操作系统书籍中,它展示了这些调度程序进入和离开的状态的一个很好的自动机。作业调度程序将作业队列中的内容放入就绪队列,CPU 调度程序将就绪队列中的内容放入运行状态。该算法就像任何其他软件一样,它必须在 CPU/核心上运行,它很可能是内核某处的一部分。

调度程序可以被抢占是没有意义的。队列内的作业在运行时可以被抢占,用于 I/O 等。不,内核不必调度自己来分配任务,它只是获取 cpu 时间而不调度自己。是的,数据很可能位于 RAM 中,不确定是否值得存储在 CPU 缓存中。