在SMP机器上,我们必须使用spin_lock_irqsave
而不是spin_lock_irq
来自中断上下文。
为什么我们要保存标志(包含IF)?
还有其他可能打断我们的中断程序吗?
答案 0 :(得分:37)
spin_lock_irqsave
主要用于在进行自旋锁之前保存中断状态,这是因为当在中断上下文中进行锁定时,自旋锁会禁用中断,并在解锁时重新启用它。保存中断状态,以便它可以再次恢复中断。
示例:
spin_lock_irq
将禁用中断x并执行锁定spin_unlock_irq
将启用中断x。因此,在释放锁定后的第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的情况下运行,
但是你不应该尝试。