【发布时间】:2016-01-24 23:50:05
【问题描述】:
我一直试图了解上下文切换在 Linux 内核中是如何工作的。在我看来,有一种情况(稍后解释)导致中断后没有调用 IRET 指令(我确信我错过了一些东西!)。我假设在中断之后调用 IRET 是非常必要的,因为在调用 IRET 之前你无法获得相同的中断。我只担心在 x86 架构上运行的单处理器内核。
我认为可能导致所描述行为的情况如下:
在内核模式下运行的进程 A 自愿调用
schedule()(例如,在尝试获取已锁定的互斥体时)。schedule()决定对进程B执行上下文切换,因此调用context_switch()context_switch()通过调用switch_mm()将虚拟内存从A切换到Bcontext_switch()运行宏switch_to()来切换堆栈并将正在运行的进程从 A 更改为 B。请注意,进程 A 现在卡在switch_to()内,并且进程 A 的堆栈看起来像(堆栈增长向下):
...
[mutex_lock()]
[schedule()]
[context_switch()] (Stack Top)
进程 B 开始运行。稍后,它收到一个定时器中断,定时器中断处理程序决定进程 B 需要重新调度。
从定时器中断返回时(但在调用 IRET 之前)
preempt_schedule_irq()被调用。preempt_schedule_irq()呼叫schedule()。schedule()决定上下文切换到进程 A 并调用context_switch()。context_switch()调用switch_mm()切换虚拟内存。context_switch()调用switch_to()来切换堆栈。此时,进程 B 的堆栈如下所示:
...
[IRET return frame]
[ret_from_interrupt()]
[preempt_schedule_irq()]
[schedule()]
[context_switch()] (Stack top)
现在进程 A 正在运行,其堆栈已恢复。由于定时器中断导致 A 中的 context_switch() 函数没有被调用,因此进程 A 不会调用 IRET 并继续执行 mutex_lock()。这种情况可能会导致定时器中断永远被阻塞。
我在这里错过了什么?
【问题讨论】:
-
自从我上次查看以来已经很久了,这是我最喜欢的主题,您是否没有考虑过作为调度程序一部分的内核计时器会发出 IRET?下载 linux 内核 v.99b IIRC 可能更容易,它是最小的源代码下载,以便实际阅读它而不会被现在的大小所淹没?
-
好吧,我知道在大多数情况下内核的计时器处理程序会发出IRET。但是,问题是调度程序并不总是由计时器调用。关于 Linux Kernel v.99b,这听起来是个不错的地方!谢谢:)
-
我不确定我是否理解您的问题,但似乎不会丢失任何 iret。由于 mutex_lock() 是一个系统调用,所以无论何时调用它,都需要使用 iret 返回用户空间(至少在使用软件中断 int 0x80 调用系统调用时是这样)。因此,当进程 A 恢复时,它将简单地完成 mutex_lock() 和 iret 的执行。
标签: c linux process kernel context-switch