我一直在尝试理解Linux内核中上下文切换的工作原理。在某些情况下(稍后会解释),似乎会导致中断后没有调用IRET指令(我确定我漏掉了什么!)。我假设在中断后调用IRET是非常必要的,因为在调用IRET之前无法获得相同的中断。我只关心在x86架构上运行的单处理器内核。
我认为可能导致所描述行为的情况如下:
- 运行在内核模式下的进程A自愿地调用schedule()(例如,在尝试获取已锁定的互斥量时)。 - schedule()决定执行到进程B的上下文切换,因此调用context_switch()。 - context_switch()通过调用switch_mm()将虚拟内存从A切换到B。 - context_switch()运行宏switch_to()来切换堆栈并实际上将运行进程从A更改为B。请注意,进程A现在被卡在switch_to()内部,进程A的堆栈看起来像(堆栈向下增长):
进程B开始运行。稍后,它接收到定时器中断,定时器中断处理程序决定进程B需要重新调度。
在定时器中断返回之前(但在调用IRET之前),调用了
我认为可能导致所描述行为的情况如下:
- 运行在内核模式下的进程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()。这种情况可能会导致计时器中断永久阻塞。
我在这里缺少什么?