我知道自旋锁在旋转时有效,存在不同的内核路径且内核是抢占式的,那么为什么自旋锁在单处理器系统中不起作用呢? (例如,在Linux中)
答案 0 :(得分:27)
如果我理解你的问题,你就会问为什么自旋锁在单核机器上是一个坏主意。
他们仍应工作,但可能比真正的线程睡眠并发更昂贵:
当你使用自旋锁时,你基本上断言你认为你不必等待很长时间。您所说的是,您认为使用繁忙的循环维护处理器时间片比将线程休眠和上下文转移到另一个线程或进程的成本更好。如果你需要等待很短的时间,你可以睡觉并且几乎立即重新唤醒,但是下降和降低的成本比仅仅等待还要贵。
这在多核处理器上更有可能,因为它们比单核处理器具有更好的并发性配置文件。在多核处理器上,在循环迭代之间,其他一些线程可能已经处理了您的先决条件。在单核处理器上,其他人不可能帮助你 - 你已经锁定了唯一的核心处理器。
这里的问题是如果你等待或睡在一个锁上,你暗示系统你还没有你需要的所有东西,所以它应该去做其他的东西然后再回到你身边即可。使用自旋锁,你从不告诉系统这个,所以你锁定它等待其他事情发生 - 但同时,你阻止整个系统 ,所以其他东西不能发生。
答案 1 :(得分:2)
自旋锁的本质是它不会对进程进行计划 - 而是旋转直到进程获得锁定。
在单处理器上,它将立即获得锁定,或者它将永久旋转 - 如果锁定是竞争的,那么当前持有资源的进程将永远不会有机会放弃它。只有当一个进程在锁定时旋转时,自旋锁才有用,这意味着多处理器系统。
答案 2 :(得分:2)
有不同版本的自旋锁:
spin_lock_irqsave(&xxx_lock, flags);
... critical section here ..
spin_unlock_irqrestore(&xxx_lock, flags);
在需要在进程上下文和中断上下文之间共享数据时,应使用Uni处理器spin_lock_irqsave()
,因为在这种情况下IRQ也会被禁用。 spin_lock_irqsave()
在所有情况下工作,但部分因为它们是安全的,它们也相当慢。
但是,如果需要跨不同的CPU保护数据,那么最好使用以下版本,这些更便宜,因为在这种情况下IRQ不会被禁用:
spin_lock(&lock);
...
spin_unlock(&lock);
在单处理器系统中,调用spin_lock_irqsave(&xxx_lock, flags);
与禁用中断具有相同的效果,这将提供所需的中断并发保护,而不需要不必要的SMP保护。但是,在多处理器系统中,这涵盖了中断和SMP并发问题。
答案 3 :(得分:1)
Spinlocks本质上是打算用于多处理器系统,尽管运行抢占式内核的单处理器工作站的行为与SMP类似,就并发性而言。如果一个非抢占式单处理器系统进入锁定旋转,它将永远旋转;没有其他线程能够获得CPU来释放锁。出于这个原因,在没有启用抢占的单处理器系统上的自旋锁操作被优化为什么都不做,除了那些改变IRQ屏蔽状态的操作。由于抢占,即使您不希望您的代码在SMP系统上运行,您仍然需要实现正确的锁定。
参考: Linux设备驱动程序 作者:Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartma
答案 4 :(得分:0)
在操作系统三个简单部分中找到以下两段可能有用的内容:
对于自旋锁,在单CPU情况下,性能开销可以是 非常痛苦想象一下持有锁的线程的情况 在一个关键部分中被抢先一步。然后可以运行调度程序 每个其他线程(想象有N - 1个其他线程),每个线程 试图获得锁定。在这种情况下,每个线程都会 在放弃CPU之前旋转一段时间片,a 浪费CPU周期。
但是,在多个CPU上,自旋锁可以正常工作 相当好(如果线程数大致等于数量 的CPU)。思路如下:想象一下CPU 1上的线程A. CPU 2上的线程B,都争用锁定。如果是线程A(CPU 1) 抓住锁,然后线程B尝试,B将旋转(在CPU 2上)。 然而,大概是批评性的部分很短,因而很快就会出现 锁定变为可用,并且由线程B获取。旋转到 等待另一个处理器上的锁定不会浪费很多周期 这种情况,因此可以有效