我试图了解linux内核中的调度过程是如何工作的 . 我的问题不是调度算法 . 它的功能 schedule()
和 switch_to()
如何工作 .
我会试着解释一下 . 我看到了:
当进程耗尽时间片时,标志 need_resched
由 scheduler_tick()
设置 . 内核检查标志,看到它已设置,并调用 schedule()
(与问题1相关)切换到新进程 . 此标志是一个消息,应该尽快调用调度,因为另一个进程应该运行 . 返回用户空间或从中断返回后,将检查 need_resched
标志 . 如果已设置,则内核会在继续之前调用调度程序 .
查看内核源代码(linux-2.6.10 - 本书"Linux Kernel Development, second edition"所基于的版本),我还看到一些代码可以自动调用 schedule()
函数,让另一个进程有权运行 . 我看到函数 switch_to()
是实际执行上下文切换的函数 . 我研究了一些与架构相关的代码,试图理解 switch_to()
实际上在做什么 .
这种行为提出了一些我无法找到答案的问题:
-
当
switch_to()
完成时,当前正在运行的进程是什么?调用schedule()
的过程?或者下一个过程,那个被选中运行的过程? -
当中断调用
schedule()
时,所选的进程在中断处理完成后开始运行(在某种RTE之后)?还是在那之前呢? -
如果无法从中断调用
schedule()
函数,则何时设置标志need_resched
? -
当定时器中断处理程序工作时,正在使用什么堆栈?
我不知道我是否能说清楚自己 . 如果我不能,我希望我可以在一些答案(或问题)之后做到这一点 . 我已经看过几个试图了解这个过程的消息来源 . 我有一本书“Linux Kernel Development,sec ed”,我也在使用它 . 我对MIP和H8300架构有所了解,如果有帮助可以解释一下 .
1 回答
调用switch_to()后,内核堆栈切换到
next
中指定的任务的内核堆栈 . 更改地址空间等在例如context_switch().中处理schedule()
无法在原子上下文中调用,包括来自中断(请参阅schedule_debug()中的检查) . 如果需要重新安排,则设置TIF_NEED_RESCHED任务标志,该标志在interrupt return path中检查 .见2 .
我相信,使用默认的8K堆栈,可以使用当前正在执行的任何内核堆栈来处理中断 . 如果使用4K堆栈,我相信's a separate interrupt stack (automatically loaded thanks to some x86 magic), but I' m在这一点上并不完全确定 .
更详细一点,这是一个实际的例子:
发生中断 . CPU切换到中断蹦床例程,将中断号推送到堆栈,然后jmps到common_interrupt
common_interrupt调用do_IRQ,其中disables preemption然后handles the IRQ
在某些时候,决定切换任务 . 这可能来自定时器中断,也可能来自唤醒呼叫 . 在任何一种情况下,都会调用set_task_need_resched,设置TIF_NEED_RESCHED任务标志 .
最终,CPU从原始中断的do_IRQ返回,并继续执行IRQ exit path.如果从内核中调用此IRQ,则checks whether TIF_NEED_RESCHED is set,如果是,则调用preempt_schedule_irq,这会在执行
schedule()
时短暂启用中断 .如果从用户空间调用IRQ,我们首先返回check whether there's anything that needs doing . 如果是这样,我们转到retint_careful,它检查挂起的重新安排(并在需要时直接调用
schedule()
)以及检查挂起的信号,然后返回retint_check的另一轮,直到没有设置更重要的标志 .最后,我们restore GS and return from the interrupt handler .
至于
switch_to()
;switch_to()
(在x86-32上)的作用是:保存EIP(指令指针)和ESP(堆栈指针)的当前值,以便稍后返回此任务 .
切换
current_task
的值 . 此时,current
现在指向新任务 .切换到新堆栈,然后将我们切换的任务保存的EIP推送到堆栈 . 之后,将使用此EIP作为返回地址执行返回;这是它如何跳回到以前称为
switch_to()
的旧代码致电__switch_to() . 此时,
current
指向新的任务,我们're on the new task'的堆栈,但各种其他CPU状态尚未更新 .__switch_to()
处理切换FPU,段描述符,调试寄存器等事物的状态 .从
__switch_to()
返回后,将返回switch_to()
手动推入堆栈的返回地址,将执行返回到新任务中switch_to()
之前的位置 . 现在,切换到任务已完全恢复执行 .x86-64非常相似,但由于ABI不同,必须稍微保存/恢复状态 .