为什么在 Linux 中的 spin_lock 和 spin_unlock 之间禁用中断?

sec*_*red 6 linux synchronization mutex interrupt atomicity

我正在阅读 Linux 信号量的实现。由于原子性,信号和等待(在源代码中向上和向下)使用自旋锁。然后我看到 Linux 在 spin_lock_irqsave 中禁用了中断并在 spin_unlock 中重新启用了中断。这让我很困惑。在我看来,在关键部分禁用中断真的没有意义。

例如,proc A(当前处于活动状态)获取了锁,proc B(阻塞)正在等待锁,proc C 正在做一些不相关的事情。在 A 和 B 之间的临界区内上下文切换到 C 是非常有意义的。即使 C 也尝试获取锁,由于锁已经被 A 锁定,结果将是 C 被阻塞而 A 恢复执行。

因此,我不知道为什么 Linux 决定禁用由自旋锁保护的临界区中的中断。它可能不会导致任何问题,但对我来说似乎是一个多余的操作。

JW *_*Lim 7

请允许我首先声明我不是 Linux 专家,所以我的回答可能不是最准确的。请指出您可能发现的任何缺陷和问题。

想象一下,如果内核的各个部分使用某些共享数据,包括需要快速且不能阻塞的中断处理程序等操作。假设系统调用foo当前处于活动状态并且已获取锁以使用/访问共享数据bar,并且在获取所述锁时/之前不会禁用中断。

现在一个(硬件)中断处理程序,例如键盘,启动并需要访问bar(硬件中断比系统调用具有更高的优先级)。由于bar当前被 syscall 锁定foo,因此中断处理程序无法执行任何操作。中断处理程序确实需要快速且不会被阻塞,因此它们只是在尝试获取锁时保持旋转,这将导致死锁(即系统冻结),因为 syscallfoo永远没有机会完成并释放其锁。

但是,如果您在尝试获取 中的锁定之前禁用中断foo,那么foo将能够完成它正在执行的任何操作并最终释放锁定(并恢复中断)。任何试图在foo持有自旋锁时进入的中断都将留在队列中,并且能够在锁被释放时启动。这样,您就不会遇到上述问题。但是,还必须注意确保锁定的bar时间尽可能短,以便其他更高优先级的操作可以在需要时接管。

  • 还值得一提的是,如果数据被多个中断处理程序使用,则应在为 **所有** 处理程序和函数获取自旋锁(使用 `spin_lock_irqsave`)之前禁用中断,这些处理程序和函数可以被也使用更高优先级的中断中断相同的数据。只有具有最高优先级的中断处理程序才不需要禁用中断。 (2认同)