你拿着自旋锁的时候为什么不能入睡?

时间:2011-01-20 20:10:52

标签: linux kernel

在linux内核中,为什么不能在拿着自旋锁时睡觉?

8 个答案:

答案 0 :(得分:16)

示例:您的驱动程序正在执行,并且刚刚取出一个锁定来控制对其设备的访问。锁定时,设备会发出中断,导致中断处理程序运行。在访问设备之前,中断处理程序还必须获得锁定。在中断处理程序中取出一个自旋锁是合法的事情;这是自旋锁操作不睡觉的原因之一。但是,如果中断例程在与最初取出锁的代码相同的处理器中执行会发生什么?当中断处理程序正在旋转时,非中断代码将无法运行以释放锁定。那个处理器将永远旋转。

来源:http://www.makelinux.net/ldd3/chp-5-sect-5.shtml

答案 1 :(得分:9)

持有旋转锁定并不是无法睡觉。这样做是一个非常非常糟糕的想法。引用LDD:

  

因此,适用于自旋锁的核心规则是任何代码在持有自旋锁时都必须是原子的。它无法入睡;事实上,它不能以任何理由放弃处理器,除了服务中断(有时甚至不是)。

如上所述的任何死锁都可能导致无法恢复的状态。可能发生的另一件事是自旋锁被锁定在一个CPU上,然后当线程休眠时,它会在另一个CPU上唤醒,导致内核崩溃。

回答Bandicoot的评论,在自旋锁上下文中,只有在单处理器可预占内核的情况下才会禁用抢占,因为禁用抢先有效防止了比赛。

  

如果在没有CONFIG_SMP的情况下编译内核,但设置了CONFIG_PREEMPT,那么自旋锁只会禁用抢占,这足以防止任何比赛。在大多数情况下,我们可以认为抢占等同于SMP,而不是单独担心它。

http://www.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/index.html

答案 2 :(得分:4)

我不同意威廉的回应(他的例子)。他混合了两个不同的概念:抢占和同步。

中断上下文可以抢占进程上下文,因此如果两者共享一个RESOURCE,我们需要使用

spin_lock_irqsave()

到(1)禁用IRQ(2)获取锁定。通过步骤1,我们可以禁用中断抢占。

我认为this线程非常有说服力。 Sleep()表示一个线程/进程将CPU和CONTEXT SWITCH的控制权交给另一个,而不释放自旋锁,这就是为什么它是错误的。

答案 3 :(得分:4)

我认为mail有一个清晰的答案:

  

由于自旋锁行为而持有自旋锁,因此无法抢占进程或睡眠状态。如果一个进程抓住一个自旋锁并在释放它之前进入睡眠状态。抓住螺旋锁的第二个进程(或中断处理程序)将忙等待。在单处理器机器上,第二个进程将锁定CPU,不允许第一个进程唤醒并释放自旋锁,以便第二个进程可以继续,它基本上是一个死锁。

答案 4 :(得分:2)

另一种可能的解释是,在自旋锁上下文中,抢占被禁用。

答案 5 :(得分:2)

除了willtate所提到的,假设一个进程在持有spilock的同时休眠。如果计划的新进程尝试获取相同的自旋锁,则会开始旋转以使锁可用。由于新进程不断旋转,因此无法安排第一个进程,因此永远不会释放锁定,从而使第二个进程永远旋转,并且我们遇到了死锁。

答案 6 :(得分:2)

关键点在Linux内核中,获取自旋锁将禁用抢占。因此,在持有自旋锁的情况下睡觉可能会导致死锁。

例如,线程A获取自旋锁。线程A在释放锁之前不会被抢占。只要线程A快速完成其工作并释放锁定,就没有问题。但是如果线程A在持有锁的同时休眠,则线程B可以被调度为运行,因为sleep函数将调用调度程序。并且线程B也可以获得相同的锁。线程B还禁用抢占并尝试获取锁定。并发生僵局。线程B将永远不会获得锁定,因为线程A持有它,并且线程A将永远不会运行,因为线程B禁用抢占。

为什么首先禁用抢占?我想这是因为我们不希望其他处理器上的线程等待太久。

答案 7 :(得分:0)

完全赞同南王。 我猜最重要的概念是“先发制人”& “调度”以及获取自旋锁时的情况。 当获得自旋锁时,抢占被禁用(我是否真实,但我认为它是正确的),这意味着定时器中断不能抢占当前的自旋锁持有者,但是当前的自旋锁仍然会调用可睡眠内核函数&主动调用调度程序&运行“另一项任务”。 如果“另一个任务”碰巧想要获得与第一个自旋锁持有者相同的自旋锁,那么问题就出现了:因为第一个自旋锁持有者已经禁用了抢占,所以通过第一个自旋锁持有者主动调用调度程序来调用“另一个任务” ,不能被抢先一步,所以它的旋转总是占用cpu,这就是死锁发生的原因。