Linux kernel 为什么在中断上下文中执行的内核代码/线程不能睡眠?

Linux kernel 为什么在中断上下文中执行的内核代码/线程不能睡眠?,linux-kernel,Linux Kernel,我正在读罗伯特·洛夫的以下文章 就是说 “…让我们讨论一下工作队列在流程上下文中运行的事实。这与其他下半部分机制不同,它们都在中断上下文中运行。在中断上下文中运行的代码无法休眠或阻塞,因为中断上下文没有用于重新调度的支持进程。因此,由于中断处理程序与进程没有关联,因此调度程序没有什么可供睡眠的,更重要的是,调度程序没有什么可供唤醒的……” 我不明白。好吧,内核中的调度程序是O(1),这是通过位图实现的。那么,是什么阻止scehduler将中断上下文置于睡眠状态并执行下一个可调度进程并将控制权传

我正在读罗伯特·洛夫的以下文章

就是说

“…让我们讨论一下工作队列在流程上下文中运行的事实。这与其他下半部分机制不同,它们都在中断上下文中运行。在中断上下文中运行的代码无法休眠或阻塞,因为中断上下文没有用于重新调度的支持进程。因此,由于中断处理程序与进程没有关联,因此调度程序没有什么可供睡眠的,更重要的是,调度程序没有什么可供唤醒的……”


我不明白。好吧,内核中的调度程序是O(1),这是通过位图实现的。那么,是什么阻止scehduler将中断上下文置于睡眠状态并执行下一个可调度进程并将控制权传递给它呢?

因为线程交换基础结构在该点上不可用。在为中断提供服务时,只有优先级较高的内容才能执行-请参阅。如果您这样做了如果要执行另一个线程(您在问题中暗示这很容易),您将无法让它执行任何操作-如果它导致页面错误,您将不得不使用内核中的服务,这些服务在服务中断时不可用(请参阅下文了解原因)

通常,在中断例程中,您的唯一目标是让设备停止中断并以较低的中断级别(在unix中,这通常是非中断级别,但在Windows中,它是调度、apc或被动级别)排队,以便在您可以访问内核/os的更多功能的地方完成繁重的工作。请参阅-


这是O/s必须如何工作的属性,而不是Linux中固有的东西。中断例程可以在任何点执行,因此中断的内容的状态不一致。如果中断线程调度代码,则其状态不一致,因此无法确定是否可以“睡眠”“并切换线程。即使您保护线程切换代码不被中断,线程切换也是O/S的一个非常高级的功能,如果您保护了它所依赖的一切,那么中断更像是一种暗示,而不是其名称所隐含的命令

那么,是什么阻止scehduler将中断上下文置于睡眠状态并执行下一个可调度进程并将控制权传递给它呢

问题在于中断上下文不是一个进程,因此不能进入睡眠状态

当中断发生时,处理器将寄存器保存到堆栈上,并跳到中断服务例程的开始。这意味着当中断处理程序正在运行时,它正在中断发生时正在执行的进程的上下文中运行。中断正在该进程的堆栈上执行,当中断处理程序完成时,该进程将恢复执行

如果您试图在中断处理程序中休眠或阻塞,那么您不仅会停止中断处理程序,还会停止它中断的进程。这可能是危险的,因为中断处理程序无法知道被中断的进程正在做什么,甚至无法知道暂停该进程是否安全

一个可能出错的简单场景是中断处理程序与其中断的进程之间的死锁

  • Process1进入内核模式
  • Process1获得了LockA
  • 中断发生
  • ISR开始使用Process1的堆栈执行
  • ISR试图收购洛克
  • ISR呼叫sleep以等待LockA释放
  • 在这一点上,您有一个死锁。在ISR完成其堆栈之前,Process1无法恢复执行。但ISR在等待Process1释放LockA时被阻塞

    那么,是什么阻止scehduler将中断上下文置于睡眠状态并执行下一个可调度进程并将控制权传递给它呢

    在计时器中断时进行调度。基本规则是一次只能打开一个中断,因此,如果您在“从设备X获取数据”中断中进入睡眠状态,则计时器中断无法运行以将其排程


    中断也会发生多次并重叠。如果将“获取数据”中断置于睡眠状态,然后获取更多数据,会发生什么?这已经足够让人困惑(也很脆弱)了,一网打尽的规则是:不要在中断中睡觉。你会弄错的。

    我认为这是一个设计理念

    当然,您可以设计一个可以在中断中睡眠的系统,但除了使系统难以理解和复杂(您必须考虑许多情况),这没有任何帮助。因此,从设计的角度来看,将中断处理程序声明为CannotSleep是非常清晰且易于实现的


    来自Robert Love(内核黑客):

    您不能在中断处理程序中睡眠,因为中断没有 一个支持进程上下文,因此没有什么需要重新安排的 进入换句话说,中断处理程序与任务无关, 因此,没有什么可以“睡觉”,更重要的是,“没有什么可以睡觉” 醒醒”。它们必须原子化运行

    这与其他操作系统没有什么不同。在大多数操作系统中, 中断不是线程化的。然而,下半部通常是

    页面错误处理程序可以休眠的原因是它仅被调用 在流程上下文中运行的代码。因为内核是自己的 内存不可分页,只有用户空间内存访问才能导致 页面错误。因此,只有少数几个特定的地方(如呼叫 复制{to,from}{u user())会导致内核中出现页面错误。那些 所有位置都必须由可以休眠的代码(即进程上下文、,
    无锁等)。

    高级中断处理程序屏蔽所有低优先级中断的操作,包括系统定时器中断的操作。因此,中断处理程序必须避免参与可能导致其sle的活动
    #linux/arch/arm/include/asm/thread_info.h 
    94 static inline struct thread_info *current_thread_info(void)
    95 {
    96  register unsigned long sp asm ("sp");
    97  return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
    98 }