spin_lock_irqsave vs spin_lock_irq

时间:2010-04-01 10:11:30

标签: linux-kernel spinlock

在SMP机器上,我们必须使用spin_lock_irqsave而不是spin_lock_irq来自中断上下文。

为什么我们要保存标志(包含IF)?

还有其他可能打断我们的中断程序吗?

6 个答案:

答案 0 :(得分:37)

spin_lock_irqsave主要用于在进行自旋锁之前保存中断状态,这是因为当在中断上下文中进行锁定时,自旋锁会禁用中断,并在解锁时重新启用它。保存中断状态,以便它可以再次恢复中断。

示例:

  1. 假设在获得自旋锁之前禁用了中断x
  2. spin_lock_irq将禁用中断x并执行锁定
  3. spin_unlock_irq将启用中断x。
  4. 因此,在释放锁定后的第3步中,我们将启用中断x,这在获取锁定之前已被禁用。

    因此,只有当您确定中断未被禁用时,您才应spin_lock_irq,否则您应始终使用spin_lock_irqsave

答案 1 :(得分:20)

我是内核的新手,但是从我从Robert Love的书“Linux内核开发”中收集到的内容,如果在代码开始锁定之前处理器上已经禁用了中断,当你调用spin_unlock_irq时,你将错误地释放锁定方式。如果您保存标志并使用标志释放它,则函数spin_lock_irqsave将仅将中断返回到其先前的状态。

spin_lock_irqsave

的示例
spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irqsave(&mLock, flags); // save the state, if locked already it is saved in flags
// Critical section
spin_unlock_irqrestore(&mLock, flags); // return to the formally state specified in flags

spin_lock_irq(没有irqsave)的示例:

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irq(&mLock); // Does not know if already locked
// Critical section
spin_unlock_irq(&mLock); // Could result in an error unlock...

答案 2 :(得分:5)

除了spin_lock_irqsave之外,spin_lock_irq的需求与local_irq_save(flags)之外的local_irq_disable非常相似。以下是Robert Love的Linux内核开发第二版对此要求的一个很好的解释。

  

如果有中断,则local_irq_disable()例程很危险   在调用之前已经禁用。相应的电话   尽管如此,local_irq_enable()无条件地启用中断   事实上,他们开始时。相反,需要一种机制   将中断恢复到以前的状态。这是一个普遍关注的问题   因为内核中的给定代码路径可以用和来达到   没有启用中断,具体取决于调用链。例如,   想象一下前面的代码片段是一个更大的函数的一部分。   想象一下,这个函数被另外两个函数调用,其中一个函数   禁用中断和不中断。因为它正在成长   随着内核在大小和复杂性方面的增长而更难以了解所有代码   导致函数的路径,保存状态更安全   禁用它之前的中断系统。然后,当你准备好了   重新启用中断,您只需将它们恢复到原始状态:

unsigned long flags;

local_irq_save(flags);    /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */
  

请注意,这些方法至少部分实现为宏,因此   flags参数(必须定义为unsigned long)是   看似通过了价值。此参数包含   包含中断状态的体系结构特定数据   系统。因为至少有一个支持的架构合并   将信息堆叠成值(ahem,SPARC),标志不能传递   到另一个函数(具体来说,它必须保持在同一个堆栈上   帧)。出于这个原因,调用save和恢复调用   中断必须出现在同一个函数中。

     

可以从中断和中调用所有以前的函数   过程背景。

答案 3 :(得分:2)

阅读链接到Robert Loves Why kernel code/thread executing in interrupt context cannot sleep?article,我读到了这个:

  

一些中断处理程序(已知)   Linux作为快速中断处理程序)运行   在本地的所有中断   处理器禁用。这样就完成了   确保中断处理程序运行   没有中断,尽快   可能。更重要的是,所有中断   处理程序与他们的当前运行   所有中断线都被禁用   处理器。这确保了两个   中断处理程序也是如此   中断线不运行   同时。它还可以防止设备   司机作家不得不处理   递归中断,这复杂化   编程。

答案 4 :(得分:0)

下面是Linux内核4.15.18中代码的一部分,该代码显示spiin_lock_irq()将调用__raw_spin_lock_irq()。但是,它不会保存任何标志,因为您可以在代码的下面看到,但是禁用了中断。

  static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
    {
        local_irq_disable();
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }

下面的代码显示spin_lock_irqsave(),它保存标志的当前阶段,然后抢先禁用。

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}

答案 5 :(得分:-1)

这个问题从错误的断言开始: On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

这两个都不应该在中断中使用 SMP或UP上的上下文。也就是说,spin_lock_irqsave() 可以从中断上下文中使用 ,因为它更通用 (可以在中断和正常情况下使用),但是 您应该在中断上下文中使用spin_lock(), 和spin_lock_irq()spin_lock_irqsave()(从正常上下文中)。 使用spin_lock_irq()几乎总是错误的事情 在中断上下文中执行此SMP或UP。可能有效 因为大多数中断处理程序都在本地启用IRQ的情况下运行, 但是你不应该尝试。