为什么拿着螺旋锁时不允许“睡觉”?

时间:2011-10-23 18:35:48

标签: c synchronization linux-kernel sleep spinlock

  

可能重复:
  Why can't you sleep while holding spinlock?

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

然而,我不知道为什么会出现这样的“规则”,即在拿着螺旋锁的时候根本不应该睡觉。我知道这不是推荐的做法(因为它对性能有害),但我认为没有理由为什么不应该让螺旋锁进入睡眠状态。

  

获取信号量时无法保持旋转锁定,因为在等待信号量时可能需要睡眠,并且在持有旋转锁定时无法入睡(来自Robert Love的“Linux内核开发”)。

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

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

3 个答案:

答案 0 :(得分:14)

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

  1. 如果线程A在自旋锁中休眠,然后线程B尝试获取相同的自旋锁,则单处理器系统将死锁。线程B将永远不会进入休眠状态(因为当A完成时,自旋锁没有唤醒B所需的等待列表),并且线程A永远不会有机会被唤醒。
  2. 自旋锁用于信号量,因为它们更有效 - 提供你不会长时间抗争。允许睡眠意味着您将有很长的争用时间,消除使用螺旋锁的所有好处。在这种情况下,只需使用信号量,您的系统就会更快。
  3. 自旋锁通常用于与中断处理程序同步,通过另外禁用中断。如果您进入中断处理程序,则无法使用此用例(您无法切换回线程以使其唤醒并完成其自旋锁临界区)。
  4. 使用合适的工具做正确的工作 - 如果你需要睡觉,信号量和互斥量是你的朋友。

答案 1 :(得分:8)

  • 实际上,可以在禁用中断或其他类型的排除活动时休眠。如果不这样做,您正在睡觉的条件可能会因中断而改变状态,然后您就永远不会醒来。睡眠代码通常永远不会输入而不是提升优先级或其他一些关键部分包含决定睡眠和上下文切换之间的执行路径。

  • 但对于自旋锁,睡眠是一种灾难,因为锁定保持不变。其他线程在击中时会旋转,直到你从睡眠中醒来它们才会停止旋转。与自旋锁中最坏情况下预期的极少数旋转相比,这可能是永恒的,因为自旋锁存在只是为了同步对存储器位置的访问,它们不应该与上下文切换机制相互作用。 br />(就此而言,每一个其他线程最终都可以击中自旋锁,然后你就会楔入整个系统每个核心的每个线程。)

答案 2 :(得分:1)

当您使用自旋锁时,您不能使用它。在真正需要保护关键区域和共享数据结构的地方使用自旋锁。如果您在持有信号量的同时获得一个,则可以锁定对您的锁附加到的任何关键区域(例如)它的访问权限(通常是特定大型数据结构的成员),同时允许此进程可能进入休眠状态。例如,如果在此进程休眠时引发IRQ,并且IRQ处理程序需要访问仍被锁定的关键区域,则它将被阻止,这在IRQ中永远不会发生。显然,你可以编写一个例子,你的自旋锁没有按照应有的方式使用(假设一个假想的自旋锁附着在一个nop环上,比如说);但这并不是Linux内核中真正的自旋锁。