spin_lock_irqsave vs spin_lock_irq

coj*_*car 26 linux-kernel spinlock

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

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

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

小智 42

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

例:

  1. 让我们说在获得自旋锁之前禁用了中断x
  2. spin_lock_irq 将禁用中断x并获取锁定
  3. spin_unlock_irq 将启用中断x.

因此,在释放锁之后的第3步中,我们将启用中断x,这在获取锁之前已被禁用.

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


Hag*_*gai 22

我是内核的新手,但是从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
Run Code Online (Sandbox Code Playgroud)

示例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...
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是不正确的.spin_lock_irq将无条件地禁用中断,而irqsave变体将在您无法知道当前状态的情况下保存中断状态. (5认同)
  • @NoahWatkins这是最后一部分的要点.它说*可能导致评论中的错误解锁*.`spin_lock_irq`表示IRQ在不应该启用时启用.答案不正确; 只是不太清楚. (4认同)
  • 像我这样的总内核noob的提示:`spin_lock_irqsave`是一个宏,而不是一个函数,这就是为什么它可以改变"*传递*"`flags`变量.这让我感到困惑,直到我在内核代码中查找它. (4认同)

4pi*_*ie0 7

需要spin_lock_irqsave除了spin_lock_irq颇为相似,其原因local_irq_save(flags)是需要之外local_irq_disable。这是Robert Love从Linux Kernel Development Second Edition中获取的对此要求的很好解释。

如果中断在调用之前已被禁用,则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 */
Run Code Online (Sandbox Code Playgroud)

请注意,这些方法至少部分地实现为宏,因此flags参数(必须定义为无符号long型)似乎是通过值传递的。此参数包含特定于体系结构的数据,其中包含中断系统的状态。因为至少一个受支持的体系结构将堆栈信息合并到值(ahem,SPARC)中,所以不能将标志传递给另一个函数(特别是,它必须保留在同一堆栈帧中)。因此,保存调用和恢复中断调用必须在同一函数中发生。

可以从中断和过程上下文中调用所有先前的函数。