为什么不在中断中使用互斥量

Why not to use mutex inside an interrupt

我已经通过 this post 我注意到在 Clifford 的回答中他说我们不应该在中断中使用互斥锁,我知道在中断中我们必须避免过多的指令和延迟 ext ...但我不是很清楚原因,谁能解释一下我们必须避免这种情况的原因?

如果我们想在 2 个中断驱动的线程之间建立同步通信,如果不允许使用互斥量,还有什么其他机制可以使用?

互斥锁通常用于确保在任何给定时间仅由一个用户使用资源。

  • 当线程需要使用资源时,它首先尝试获取互斥量以确保资源可用。如果互斥量不可用,则线程通常会阻塞以等待互斥量可用。

  • 当一个线程拥有互斥锁时,它会阻止其他线程获取互斥锁并干扰它对资源的使用。更高优先级的线程通常是这里的关注点,因为这些线程可能会抢占互斥所有者。

  • RTOS 内核将互斥量的所有权分配给特定线程,通常只有互斥量所有者才能释放互斥量。

现在让我们从中断处理程序的角度来想象一下。

  • 如果中断处理程序试图获取不可用的互斥量,它应该怎么办?中断处理程序不能像线程一样阻塞(内核不具备推送中断处理程序上下文或从中断处理程序切换到线程的能力)。

  • 如果中断处理程序获得了互斥锁,那么有什么更高优先级的代码可以中断中断处理程序并尝试使用互斥锁?中断处理程序是否会在完成之前释放互斥体?

  • 内核如何将互斥锁的所有权分配给中断处理程序?中断处理程序不是线程。如果中断处理程序不释放互斥锁,那么内核将如何验证所有者正在释放互斥锁?

也许您对所有这些问题都有答案。也许您可以保证中断处理程序仅在互斥锁可用时运行,或者中断处理程序不会阻塞在互斥锁上。或者,也许您正试图保护资源访问免受更高优先级的嵌套中断处理程序的影响,该中断处理程序也想使用该资源。也许您的内核在分配所有权或限制谁释放互斥体方面没有任何障碍。我想如果您已经回答了所有这些问题,那么也许您有理由在中断处理程序中使用互斥体。

但也许您真正需要的是信号量。信号量的一个常见应用是发出事件信号。信号量在中断处理程序中经常以这种方式使用。中断处理程序发布或设置信号量以发出事件已发生的信号。线程在信号量上挂起以等待事件条件。 (信号量没有互斥体所具有的所有权限制。)事件信号量是在 2 个中断驱动线程之间建立同步通信的一种常用方法。

您引用的原始问题是指 Atmel ATMegaAVR 上的代码——一个简单的 8 mit 微控制器。在这种情况下,可以假设互斥机制是简单 RTOS 的一部分。

在这样的系统中,有线程上下文和中断上下文。中断由硬件调用,而线程由 RTOS 调度器调度。现在当中断发生时,任何线程都会立即pre-empted;中断必须 运行 完成并且只能被更高优先级的中断抢占(支持嵌套中断)。所有挂起的中断将 运行 在调度程序可以 运行.

之前完成

阻塞互斥体(或任何阻塞内核对象)是一个调度事件。如果您要在中断中进行任何阻塞调用,调度程序将永远不会 运行。在实践中,RTOS 将忽略阻塞调用、引发异常或进入终端错误处理程序。

一些 OS,例如 SMX、Velocity 甚至 WinCE,具有更复杂的中断架构并支持各种 延迟中断处理程序 。延迟中断处理程序是 run-to-completion 从中断调度的,但 运行 在中断上下文之外;在此类处理程序中阻止的规则可能不同,但您需要参考特定的 OS 文档。如果没有延迟中断处理程序,通常的解决方案是让线程等待某个阻塞对象(例如信号量),并让中断本身做更多的事情来导致对象解除阻塞(例如给出信号量)。

Multi-processor/core 和并行处理系统完全是另一个问题,这样的系统远远超出了最初发表评论的问题范围,也超出了我的经验——我的评论可能不适用于这样的系统,但毫无疑问,在任何情况下都存在额外的复杂性和考虑因素

虽然在中断处理程序中使用互斥体可能会出现问题,但这是很常见的做法,没有任何问题。

它只在多核系统上才有意义。只有一个核心(没有 hyper-threading),互斥体无论如何都不会做任何事情。如果核心是 运行 获取中断代码可以获取的互斥锁的代码,则无论如何都会禁用中断(或重要的中断子集)。因此只有一个内核,互斥量永远不会发生任何争用。

但是,对于多个内核,通常使用互斥锁来保护中断和 non-interrupt 代码之间通信的结构。只要您知道自己在做什么,如果您要编写中断处理程序就必须知道,就没有问题。

互斥体如何阻塞和解除阻塞在很大程度上取决于实现。它可以让 CPU 进入睡眠状态并被 inter-process 中断唤醒。它可以以某种 CPU-specific 的方式旋转 CPU。

请注意,一个经常与此混淆的完全不相关的概念是在 user-space 信号处理程序中使用 user-space 互斥体。那是一个完全不同的问题。

术语“互斥锁”通常被定义为执行上下文之间最简单的同步形式,同时也是一种结构,它不仅会检查资源是否可用,还会等待资源可用(如果可用)不是,一旦可用就立即获取它。这些定义是不一致的,因为最简单的同步形式只涉及测试一个人是否已被授予资源所有权,如果没有,则不提供任何内置机制来等待它。

在中断处理程序中包含等待资源可用的代码几乎是不合适的,除非唯一可以持有资源的是更高优先级的中断或将自发释放资源的硬件。如果术语“互斥锁”仅用于描述此类构造,那么很少有人可以在中断处理程序中正确使用互斥锁。然而,如果人们更广泛地使用术语“互斥”来指代最简单的结构,这种结构将确保访问资源的一段代码只能在宇宙中任何地方都没有其他代码访问该资源的时候执行资源,那么在中断中使用此类结构通常不仅是正确的,而且是必需的。