我一直在努力理解上下文切换在 Linux 内核中是如何工作的。在我看来,有一种情况(稍后解释)导致在中断后没有调用 IRET 指令(我确信我遗漏了什么!)。我假设在中断之后调用 IRET 是非常必要的,因为在调用 IRET 之前您无法获得相同的中断。我只担心在 x86 arch 上运行的单处理器内核。
我认为可能导致所述行为的情况如下:
在内核模式下运行的进程 A 主动调用
schedule()
(例如,在尝试获取已锁定的互斥量时)。schedule()
决定执行进程 B 的上下文切换,因此调用context_switch()
context_switch()
通过调用switch_mm()
将虚拟内存从 A 切换到 B
context_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()。这种情况可能会导致永远阻塞定时器中断。
我在这里错过了什么?
最佳答案
经济的真实时间,非 linux 特定的解释/示例:
线程 A 不必调用 IRET - 内核代码调用 IRET 以将执行返回给线程 A,毕竟,这是它可能首先丢失它的一种方式 - 来自某些外围设备的硬件中断。
通常,当线程 A 由于某些其他硬件中断或 sycall 而早些时候丢失执行时,线程 A 的堆栈指针保存在内核 TCB 中,指向 A 堆栈上的 IRET 返回帧,然后再切换到内核堆栈内部调度程序等 gubbins。如果由于使用的特定系统调用机制不存在确切的 IRET 帧,则组装一个。当内核需要恢复 A 时,内核将线程 A 存储的 SP 和 IRET 的硬件 SP 重新加载到用户空间。工作完成 - A 恢复运行并启用中断等。
然后内核失去了控制。当它被下一个硬件中断/驱动程序或系统调用再次进入时,它可以将它的内部 SP 设置到它自己的私有(private)堆栈的顶部,因为它在调用之间不保留任何状态数据。
这只是它可以工作的一种方式:) 显然,确切的机制依赖于 ABI/架构。
关于c - 谁在 Linux 内核上下文切换后调用 IRET?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33336274/