你为什么不能在睡觉的时候睡觉?

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

Solutions Collecting From Web of "你为什么不能在睡觉的时候睡觉?"

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

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

不是你拿着自旋锁不能睡觉。 这样做是一个非常糟糕的主意。 引用LDD:

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

像上面提到的任何死锁都可能导致不可恢复的状态。 另一件可能发生的事情是,自旋锁被锁定在一个CPU上,然后当线程休眠时,它在另一个CPU上唤醒,导致内核恐慌。

回答Bandicoot的评论是,在自旋锁定环境下, 只有在单处理器可抢占内核的情况下,先占权才被禁用,因为禁用抢占有效地阻止了竞赛。

如果内核编译时没有CONFIG_SMP,但是CONFIG_PREEMPT被设置,那么自旋锁只是禁止抢占,这足以阻止任何比赛。 对于大多数目的而言,我们可以认为抢先等同于SMP,而不是单独担心。

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

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

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

spin_lock_irqsave() 

(1)禁用IRQ(2)获取锁。 通过第一步,我们可以禁止中断抢占。

我认为这个线程是非常有说服力的。 Sleep()意味着一个线程/进程产生CPU和上下文切换控制到另一个,而不释放螺旋锁,这就是为什么它是错误的。

另一个可能的解释是,在螺旋锁的情况下,先占权被禁用。

除了上面提到的威廉姆斯之外,假设一个进程睡觉的时候拿着一个spilock。 如果计划的新进程尝试获取相同的螺旋锁,则开始旋转以使锁可用。 由于新进程继续旋转,所以不可能安排第一个进程,因此永远不会释放锁,使得第二个进程永远旋转,我们陷入僵局。

我觉得这封邮件有一个清晰的答案:

一个进程不能被抢占,也不能睡觉,同时举行螺旋锁定自旋锁的行为。 如果一个进程抓住一个螺旋锁,并在释放它之前去睡觉。 第二个进程(或中断处理程序),以获取螺旋锁将忙于等待。 在单处理器机器上,第二个进程将锁定CPU,不允许第一个进程唤醒并释放螺旋锁,所以第二个进程可以继续,这基本上是一个死锁。

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

例如,线程A获取自旋锁。 线程A不会被抢占,直到释放锁。 只要线程A快速完成任务并释放锁,就没有问题了。 但是,如果线程A在按住锁的同时休眠,则线程B可以被调度为运行,因为睡眠函数将调用调度程序。 线程B也可以获得相同的锁。 线程B也禁用抢占,并尝试获取锁。 并发生死锁。 线程B永远不会得到锁,因为线程A持有它,线程A将永远不会运行,因为线程B禁用抢占。

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

完全同意王楠。 我想最重要的概念是“抢占”和“调度”,以及如何获得自旋锁时发生。 当spinlock被获取时,抢占被禁止(true或not,我不知道,但是假设它是正确的),这意味着timer中断不能抢占当前的spinlock持有者,但是当前的spinlock持续仍然调用sleepble的内核函数并主动调用调度程序和运行“另一个任务”。 如果“另一个任务”想要获得与第一个自旋锁持有者相同的自旋锁,则存在问题:由于先占自锁锁定者已经禁止了抢先,由第一自旋锁持有者主动调用调度器来调用“另一个任务” ,不能被抢占,所以它的旋转总是拿着CPU,这就是为什么会发生死锁。