Linux 为什么你拿着自旋锁睡不着?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4752031/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 02:34:03  来源:igfitidea点击:

Why can't you sleep while holding spinlock?

linuxkernel

提问by Bandicoot

In the linux kernel, why can't you sleep while holding a spinlock?

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

采纳答案by Will Tate

Example: your driver is executing and has just taken out a lock that controls access to its device. While the lock is held, the device issues an interrupt, which causes your interrupt handler to run. The interrupt handler, before accessing the device, must also obtain the lock. Taking out a spinlock in an interrupt handler is a legitimate thing to do; that is one of the reasons that spinlock operations do not sleep. But what happens if the interrupt routine executes in the same processor as the code that took out the lock originally? While the interrupt handler is spinning, the noninterrupt code will not be able to run to release the lock. That processor will spin forever.

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

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

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

回答by TheLoneJoker

Another likely explanation is that, in a spinlock context pre-emption is disabled.

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

回答by San

Apart from what willtate has mentioned, assume that a process sleeps while holding a spilock. If the new process that is scheduled tries to acquire the same spinlock, it starts spinning for the lock to be available. Since the new process keeps spinning, it is not possible to schedule the first process and thus the lock is never released making the second process to spin for ever and we have a deadlock.

除了 willtate 提到的内容,假设一个进程在持有一个 spilock 时休眠。如果被调度的新进程尝试获取相同的自旋锁,它会开始自旋以使锁可用。由于新进程一直在旋转,因此无法调度第一个进程,因此永远不会释放锁,从而使第二个进程永远旋转,从而导致死锁。

回答by Omair

It's not that you can'tsleep while holding a spin lock. It is a very very badidea to do that. Quoting LDD:

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

Therefore, the core rule that applies to spinlocks is that any code must, while holding a spinlock, be atomic. It cannot sleep; in fact, it cannot relinquish the processor for any reason except to service interrupts (and sometimes not even then).

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

Any deadlock like mentioned above may result in an unrecoverable state. Another thing that could happen is that the spinlock gets locked on one CPU, and then when the thread sleeps, it wakes up on the other CPU, resulting in a kernel panic.

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

Answering Bandicoot's comment, in a spin lock context, pre-emption is disabled only in case of a uniprocessor pre-emptible kernelbecause disabling pre-emption effectively prevents races.

回答 Bandicoot 的评论,在自旋锁上下文中,仅在单处理器可抢占内核的情况下禁用抢占,因为禁用抢占有效地防止了竞争

If the kernel is compiled without CONFIG_SMP, but CONFIG_PREEMPT is set, then spinlocks simply disable preemption, which is sufficient to prevent any races. For most purposes, we can think of preemption as equivalent to SMP, and not worry about it separately.

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

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

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

回答by Qylin

I disagree with William's response (his example). He's mixing two different concepts: preemption and synchronization.

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

An Interrupt Context could preempt a Process Context and thus if there a RESOURCE shared by the both, we need to use

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

spin_lock_irqsave()

to (1) disable the IRQ (2) acquire the lock. By step 1, we could disable interrupt preemption.

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

I think thisthread is much convincing. Sleep() means a thread/process yields the control of the CPU and CONTEXT SWITCH to another, without releasing the spinlock, that's why it's wrong.

我觉得这个话题很有说服力。Sleep() 意味着一个线程/进程将 CPU 和 CONTEXT SWITCH 的控制权交给另一个,而不释放自旋锁,这就是为什么它是错误的。

回答by Nan Wang

The key point is in Linux kernel, acquiring a spin lock will disable preemption. Thus sleeping while holding a spin lock could potentially cause deadlock.

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

For example, thread A acquires a spin lock. Thread A will not be preempted until it releases the lock. As long as thread A quickly does its job and releases the lock, there is no problem. But if thread A sleeps while holding the lock, thread B could be scheduled to run since the sleep function will invoke the scheduler. And thread B could acquire the same lock as well. Thread B also disables preemption and tries to acquire the lock. And a deadlock occurs. Thread B will never get the lock since thread A holds it, and thread A will never get to run since thread B disables preemption.

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

And why disabling preemption in the first place? I guess it's because we don't want threads on other processors to wait too long.

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

回答by steve

total agree with Nan Wang. I guess most important concept is "preemption" & "scheduling" and how happen when spinlock is acquired. when spinlock is acquired, preemption is disabled(true or not, I don't know, but assume it is correct), it means timer interrupt can't preempt current spinlock holder, but current spinlock hold still call sleepable kernel functions & actively invoke scheduler & run "another task". if "another task" happened to want to acquire the same spinlock as the first spinlock holder, here is problem come: since preemption is already disabled by first spinlock holder, "another task" which is invoked by actively call of scheduler by first spinlock holder, can't be preempted out, so its spinning always take the cpu, this is why deadlock happen.

总同意南王的说法。我想最重要的概念是“抢占”和“调度”以及获取自旋锁时会发生什么。当获取自旋锁时,抢占被禁用(真与否,我不知道,但假设它是正确的),这意味着定时器中断不能抢占当前自旋锁持有者,但当前自旋锁持有者仍会调用可休眠的内核函数并主动调用调度程序并运行“另一个任务”。如果“另一个任务”碰巧想要获得与第一个自旋锁持有者相同的自旋锁,那么问题来了:由于抢占已经被第一个自旋锁持有者禁用,“另一个任务”通过第一个自旋锁持有者主动调用调度程序来调用,不能被抢占,所以它的旋转总是占用cpu,这就是死锁发生的原因。

回答by Nan Xiao

I think this mailhas a clarity answer:

我认为这封邮件有一个明确的答案:

A process cannot be preempted nor sleep while holding a spinlock due spinlocks behavior. If a process grabs a spinlock and goes to sleep before releasing it. A second process (or an interrupt handler) that to grab the spinlock will busy wait. On an uniprocessor machine the second process will lock the CPU not allowing the first process to wake up and release the spinlock so the second process can continue, it is basically a deadlock.

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