【问题标题】:Who calls IRET after context switch in Linux Kernel?Linux内核中上下文切换后谁调用IRET?
【发布时间】:2016-01-24 23:50:05
【问题描述】:

我一直试图了解上下文切换在 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 的堆栈看起来像(堆栈增长向下):


 ...
 [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


【解决方案1】:

经济实惠,非 linux 特定的解释/示例:

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

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

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

这只是使其工作的一种方式:) 显然,确切的机制/s 取决于 ABI/架构。

【讨论】:

  • 感谢您的回答。您的意思是在调用调度程序之前 IRET 框架始终存在吗?换句话说,你的意思是如果没有从中断处理程序调用调度程序,就会显式创建 IRET 帧?
  • 嗯,这取决于中断/系统调用架构。如果系统调用是由软件中断(例如 INT 80H)执行的,则已经存在合适的帧。如果出于某种原因需要(例如,为所有中断优先级正确启用处理器内部中断硬件),要执行 IRET 并且不存在正确的帧,则显式组装一个相当容易:) 类似的机制用于运行一个新线程 - 它以前从未运行过,因此不能有任何框架,因此必须在其新堆栈上建立一个框架,以便可以通过 IRET 来运行它。
【解决方案2】:

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

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

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

【讨论】:

  • 感谢解答,我同意如果IRET返回dispatcher没有问题。但是,在浏览了 Linux 的上下文切换代码之后,我很确定 Linux 会在 中断处理程序返回之前执行上下文切换。另外,我并不是说中断处理程序执行上下文切换。我的意思是在中断处理程序返回之前调用调度程序。
  • @harshadshirwadkar - 我更新了我的答案,指出中断处理程序通过调度程序“退出”(调度程序执行 IRET)。我稍后会删除此评论。
猜你喜欢
  • 1970-01-01
  • 2019-03-17
  • 2019-06-10
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多