来自linux内核的schedule()+ switch_to()函数如何实际工作?

der*_*erf 28 context-switch linux-kernel

我试图了解linux内核中的调度过程是如何工作的.我的问题不是调度算法.它关于功能schedule()switch_to()工作方式.

我会试着解释一下.我看到了:

当进程耗尽时间片时,标志need_resched由设置scheduler_tick().内核检查标志,看到它已设置,并调用schedule()(与问题1相关)切换到新进程.此标志是应该尽快调用计划的消息,因为另一个进程应该运行.返回用户空间或从中断返回后,将need_resched检查该标志.如果已设置,则内核会在继续之前调用调度程序.

查看内核源代码(linux-2.6.10 - "Linux内核开发,第二版"所依据的版本),我还看到一些代码可以自动调用该schedule()函数,让另一个进程有权运行.我看到该函数switch_to()是实际进行上下文切换的函数.我查看了一些与架构相关的代码,试图了解switch_to()实际上在做什么.

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

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

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

  3. 如果schedule()无法从中断调用该函数,那么何时是标志need_resched设置?

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

我不知道我是否能说清楚自己.如果我不能,我希望我可以在一些答案(或问题)之后做到这一点.我已经看过几个试图了解这个过程的消息来源.我有一本书"Linux Kernel Development,sec ed",我也在使用它.我对MIP和H8300架构有所了解,如果有帮助可以解释一下.

bdo*_*lan 31

  1. 调用之后switch_to(),内核堆栈将切换到名为的任务next.改变地址空间等在例如处理context_switch().
  2. schedule()无法在原子上下文中调用,包括来自中断(请参阅签入schedule_debug()).如果需要重新安排,则设置TIF_NEED_RESCHED任务标志,该标志在中断返回路径中被检查.
  3. 见2.
  4. 我相信,使用默认的8K堆栈,可以使用当前正在执行的任何内核堆栈来处理中断.如果使用4K堆栈,我相信有一个单独的中断堆栈(由于一些x86魔法自动加载),但我不完全确定这一点.

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

  1. 发生中断.CPU切换到中断蹦床例程,该例程将中断号压入堆栈,然后jmp到common_interrupt
  2. common_interrupt调用do_IRQ,它禁用抢占然后处理IRQ
  3. 在某些时候,决定切换任务.这可能来自定时器中断,也可能来自唤醒呼叫.在任何一种情况下,都会调用set_task_need_resched,设置TIF_NEED_RESCHED任务标志.
  4. 最终,CPU从原始中断的do_IRQ返回,并进入IRQ退出路径.如果从内核中调用此IRQ,它将检查是否设置了TIF_NEED_RESCHED,如果,则调用preempt_schedule_irq,它会在执行时短暂启用中断schedule().
  5. 如果从用户空间调用IRQ,我们首先检查在返回之前是否有任何需要做的事情.如果是这样,我们转到retint_careful,它检查挂起的重新安排(并schedule()在需要时直接调用)以及检查挂起的信号,然后返回另一轮,retint_check直到没有更多重要的标志设置.
  6. 最后,我们恢复GS并从中断处理程序返回.

至于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不同,必须稍微保存/恢复状态.