c - 谁在 Linux 内核上下文切换后调用 IRET?

标签 c linux process kernel context-switch

我一直在努力理解上下文切换在 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/

相关文章:

c - 如何将 typedef 结构与实例创建结合起来?

linux - 确定 Linux 上进程停滞的原因

c - 如何创建多个进程

windows - 如何休眠应用程序?

c - 从使用调试选项编译的 C 代码获取源代码

c++ - 当一个线程希望执行或打印某些内容时如何暂停其他线程?

c - 如何通过欧几里德算法计算两个数的gcd

linux - Ansible 中用于在远程主机上复制文件的同步模块

c++ - 从代码中获取像/proc/interrupts 这样的中断计数器?

c++ - 如何从控制台读取行并将其存储到 C++ 中的字符串中?