首页 文章

谁在Linux内核中进行上下文切换后调用IRET?

提问于
浏览
3

我一直试图理解上下文切换在Linux内核中是如何工作的 . 在我看来,有一种情况(后面会解释)导致在中断后没有调用IRET指令(我确信有些东西我错过了!) . 我假设在中断之后调用IRET是非常必要的,因为在调用IRET之前你不能得到相同的中断 . 我只担心在x86 arch上运行的单处理器内核 .

我认为可能导致所述行为的情况如下:

  • 进程在内核模式下运行会自动调用 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() . 这种情况可能会导致永久阻止定时器中断 .

我在这里想念的是什么?

2 回答

  • 1

    我不了解Linux,但在许多操作系统中,上下文切换通常由调度程序执行,而不是中断处理程序 . 如果中断没有导致挂起的上下文切换,它只会返回 . 如果需要中断触发的上下文切换,则保存当前状态,并通过调度程序退出中断(调度程序执行IRET) . 如果允许嵌套中断,则会变得更复杂,因为初始中断是进入调度程序的中断,无论哪个嵌套中断处理程序触发上下文切换条件 . 中断需要检查保存的状态以查看它是否是嵌套中断,如果不是,它可以禁用中断以防止在检查时发生嵌套中断,并可选择通过调度程序退出以执行上下文切换 . 如果中断是嵌套中断,则只需在需要时设置上下文切换标志,并依赖初始中断进行检查和上下文切换 .

    通常,除非要发生上下文切换,否则不需要中断来在内核TCB中保存线程状态 .

    调度程序还处理上下文切换由非中断条件触发的情况,例如互斥,信号量等 .

  • 1

    经济实用的时间,非linux特定的解释/例子:

    线程A不必调用IRET - 内核代码调用IRET将执行返回给线程A,毕竟,这是它可能首先丢失它的一种方式 - 来自某些外围设备的硬件中断 .

    通常,当线程A由于某些其他硬件中断或sycall而在之前丢失执行时,线程A的堆栈指针保存在内核TCB中,指向A堆栈上的IRET返回帧,然后切换到所有内部调度程序的内核堆栈古比斯 . 如果由于使用了特定的系统调用机制而不存在精确的IRET帧,则组装一个 . 当内核需要恢复A时,内核将带有线程A的存储SP和IRET的硬件SP重新加载到用户空间 . 已完成工作 - 已启用中断运行的恢复 .

    然后内核失去了控制权 . 当它被下一个硬件中断/驱动程序或系统调用再次输入时,它可以将其内部SP设置为其自己的私有堆栈的顶部,因为它在调用之间不保留任何状态数据 .

    这只是它可以工作的一种方式:)显然,确切的机制是依赖于ABI /架构 .

相关问题