2011-06-29 32 views
26

我想了解如何在Linux内核的计划进程实际工作。我的问题不是关于调度算法。它关于功能schedule()switch_to()是如何工作的。Linux内核的schedule()+ switch_to()函数是如何工作的?

我会尽力解释。我看到:

当进程用完时间片时,标记need_reschedscheduler_tick()设置。内核检查标志,看到它已设置,并调用schedule()(与问题1相关)切换到新进程。该标志是应该尽快调用调度的消息,因为另一个进程值得运行。 返回到用户空间或从中断返回时,将检查need_resched标志。如果已设置,则内核在继续之前调用调度程序。看看内核源代码(linux-2.6.10-“Linux Kernel Development,第二版”所基于的版本),我还看到有些代码可以自动调用schedule()函数,给出另一个进程运行权。 我看到函数switch_to()是实际进行上下文切换的函数。我研究了一些依赖于架构的代码,试图了解switch_to()实际上在做什么。

这种行为提出了一些问题,我无法找到答案:

  1. switch_to()结束,什么是当前正在运行的进程?这个过程叫做schedule()?或者下一个过程,即被选中运行的过程?

  2. schedule()被中断调用时,当中断处理完成时(在某种RTE之后),选择的进程运行开始运行?或之前?

  3. 如果schedule()函数不能从中断中调用,何时设置了flag-need_resched

  4. 当定时器中断处理程序正在工作时,正在使用什么堆栈?

我不知道我是否可以说清楚。如果我不能,我希望我能在回答(或问题)后做到这一点。 我已经看了几个试图了解这个过程的来源。我有这本书“Linux Kernel Development,sec ed”,我也在使用它。 我知道一些关于MIP和H8300架构,如果有帮助解释。

回答

24
  1. 调用switch_to()后,内核堆栈被切换到在next命名的任务。更改地址空间等在context_switch().中处理
  2. schedule()不能在原子上下文中调用,包括来自中断(请参阅schedule_debug()中的检查)。如果需要重新计划,则设置TIF_NEED_RESCHED任务标志,在interrupt return path中检查该标志。
  3. 请参阅2.
  4. 我相信,使用默认的8K堆栈,中断将使用当前正在执行的任何内核堆栈进行处理。如果使用4K堆栈,我相信有一个单独的中断堆栈(自动加载感谢一些x86魔法),但我不完全确定这一点。

更详细一点,这里有一个实际的例子:

  1. 中断发生。 CPU切换到一个中断例行蹦床,其推动中断号压入堆栈,然后JMPS到common_interrupt
  2. common_interrupt调用do_IRQ,其disables preemption然后handles the IRQ
  3. 在某一时刻,作出决定以切换任务。这可能来自定时器中断或来自唤醒呼叫。无论哪种情况,都会调用set_task_need_resched,并设置TIF_NEED_RESCHED任务标志。
  4. 最后,从do_IRQ原中断的CPU的回报,并前进到IRQ exit path.如果该IRQ从内核中调用,它checks whether TIF_NEED_RESCHED is set,如果有来电preempt_schedule_irq,其中简要能中断在执行schedule()
  5. 如果从用户空间调用IRQ,我们首先在check whether there's anything that needs doing之前返回。如果是这样,我们去retint_careful,它检查两个待处理的重新计划(如果需要,直接调用schedule())以及检查待处理的信号,然后返回到retint_check的另一轮,直到没有更重要的标志设置为止。
  6. 最后,我们restore GS and return from the interrupt handler

至于switch_to();什么switch_to()(上X86-32)的作用是:

  1. 保存EIP(指令指针)和ESP(堆栈指针)的因为当我们在某个时候回到这个任务后的当前值。
  2. 切换值current_task。此时,current现在指向新任务。
  3. 切换到新堆栈,然后将由我们切换的任务保存的EIP推入堆栈。之后,将使用此EIP作为退货地址执行退货;这是它如何跳回到之前称为的旧代码switch_to()
  4. 请致电__switch_to()。此时,current指向新任务,并且我们处于新任务的堆栈中,但其他各种CPU状态尚未更新。 __switch_to()处理切换像FPU,段描述符,调试寄存器等事物的状态。
  5. __switch_to()返回后,switch_to()手动推入堆栈的返回地址返回,将执行放回到之前的位置switch_to()在新任务中。切换任务现在已完全恢复执行。

x86-64非常相似,但由于ABI不同,必须稍微进行更多的状态保存/恢复。

+0

对不起,我还是不明白。 例如: 假设我们有一个任务'A'正在运行。 1 - 发生定时器中断。 2 - 定时器中断处理程序启动。 3 - 现在是时间调用schedule()并且我们来做。(),switch_to()已经完成,任务'B'是当前任务(现在我们正在使用任务'B'的堆栈,并且我们仍在运行中断代码) 。 5 - 定时器中断结束,并恢复执行任务'B'。 这个例子正确吗?如果不是,过程如何发生? – derf

+0

谢谢你的时间和耐心,但我想我错过了一些观点。你提供的信息比我现在可以处理的更多(当然,我对此非常感兴趣)。你能以更一般的方式解释吗? 我真的不明白的是:switch_to()完成后会发生什么? 在中断代码返回之前,所选任务(下一个)是否开始运行?哪个堆栈是当前的? – derf

+0

我想我明白了。为了保持直线,我们没有中断,让我们忽略所有其他细节,并且只使用switch_to()。假设我有两个任务A和B,他们都有10个指令要做。任务A正在执行指令3,任务B正在执行指令6.任务A是当前任务。因此,任务A调用switch_to(),在指令3中保留任务A,并在指令6中恢复任务B.任务B跳到7,调用switch_to(),在7保留B,并在3恢复A.任务A去到4,再次调用switch_to(),并且进程继续进行。那是对的吗? – derf