7

可能重复:
为什么拿着自旋锁不能睡觉?

据我所知,自旋锁应该在短时间内使用,并且只是代码中的选择,例如不允许休眠(抢占)的中断处理程序。

但是,我不知道为什么会有这样一个“规则”,即在持有自旋锁时根本不应该睡觉。我知道这不是推荐的做法(因为它不利于性能),但我认为没有理由不应该在自旋锁中允许睡眠。

在获取信号量时不能持有自旋锁,因为在等待信号量时可能需要休眠,并且在持有自旋锁时不能休眠(来自 Robert Love 的“Linux Kernel Development”)。

我能看到的唯一原因是出于可移植性的原因,因为在单处理器中,自旋锁被实现为禁用中断,并且通过禁用中断,当然不允许休眠(但休眠不会破坏 SMP 系统中的代码)。

但我想知道我的推理是否正确,或者是否还有其他原因。

4

3 回答 3

14

至少在 Linux 中,不允许在自旋锁中休眠有几个原因:

  1. 如果线程 A 在自旋锁中休眠,然后线程 B 尝试获取相同的自旋锁,则单处理器系统将死锁。线程 B 永远不会进入睡眠状态(因为当 A 完成时,自旋锁没有唤醒 B 所需的等待列表),并且线程 A 永远不会有机会醒来。
  2. 自旋锁被用于信号量正是因为它们更有效——前提是你不会长时间竞争。允许休眠意味着您将有很长的争用期,从而消除使用自旋锁的所有好处。在这种情况下,仅使用信号量,您的系统会更快。
  3. 自旋锁通常用于通过额外禁用中断来与中断处理程序同步。如果您休眠,则此用例是不可能的(一旦您进入中断处理程序,您就无法切换回线程让它唤醒并完成其自旋锁临界区)。

为正确的工作使用正确的工具 - 如果您需要睡觉,信号量和互斥锁是您的朋友。

于 2011-10-23T18:41:04.067 回答
8
  • 实际上,您可以在禁用中断或其他类型的排除活动的情况下睡觉。如果您不这样做,您正在睡觉的条件可能会由于中断而改变状态,然后您将永远不会醒来。如果没有提升的优先级或包含决定睡眠和上下文切换之间的执行路径的其他一些关键部分,则通常永远不会输入睡眠代码。

  • 但是对于自旋锁来说,睡眠是一场灾难,因为锁一直处于设置状态。其他线程在碰到它时会旋转,并且在您从睡眠中醒来之前它们不会停止旋转。与自旋锁最坏情况下预期的少数自旋相比,这可能是永恒的,因为自旋锁的存在只是为了同步对内存位置的访问,它们不应该与上下文切换机制交互。

    (就此而言,所有其他线程最终都可能遇到自旋锁,然后您将楔入整个系统的每个核心的每个线程。)

于 2011-10-23T18:41:19.400 回答
1

当您使用自旋锁时,您不能这样做,因为它应该被使用。自旋锁用于真正需要保护关键区域和共享数据结构的地方。如果您在持有信号量的同时获得一个信号量,则锁定对您的锁定所附加到的任何关键区域(例如)的访问(它通常是特定较大数据结构的成员),同时允许该进程可能进入睡眠状态。例如,如果在此进程休眠时引发了 IRQ,并且 IRQ 处理程序需要访问仍被锁定的关键区域,则它会被阻塞,而 IRQ 永远不会发生这种情况。显然,您可以编造一些示例,说明您的自旋锁没有按应有的方式使用(例如,附加到 nop 循环的假设自旋锁);但这根本不是 Linux 内核中真正的自旋锁。

于 2011-10-23T18:49:06.863 回答