我在具有 1 个 CPU 和非抢占式 linux 内核(2.6.x)的系统上读到,spin_lock 调用相当于一个空调用,因此以这种方式实现。
我无法理解:它不应该等同于在互斥体上休眠吗?例如,即使在非抢占式内核上,中断处理程序仍可能会被执行,或者我可能会调用一个使原始线程进入睡眠状态的函数。因此,如果将空的 spin_lock 调用实现为互斥锁,则它不是“安全的”。
有什么我不明白的吗?
我在具有 1 个 CPU 和非抢占式 linux 内核(2.6.x)的系统上读到,spin_lock 调用相当于一个空调用,因此以这种方式实现。
我无法理解:它不应该等同于在互斥体上休眠吗?例如,即使在非抢占式内核上,中断处理程序仍可能会被执行,或者我可能会调用一个使原始线程进入睡眠状态的函数。因此,如果将空的 spin_lock 调用实现为互斥锁,则它不是“安全的”。
有什么我不明白的吗?
要回答您问题的两个部分:
即使在非抢占式内核上,中断处理程序仍可能被执行,例如......
spin_lock()
不应该防止中断处理程序 - 只有用户上下文内核代码。 spin_lock_irqsave()
是中断禁用版本,这不是非抢占式单处理器上的空操作。
...或者我可能会调用一个使原始线程进入睡眠状态的函数。
持有自旋锁时不允许休眠。这是“原子时调度”错误。如果您想睡觉,则必须改用互斥锁(同样-这些不是非抢占式单处理器上的无操作)。
如果您要spin_lock()
在非抢占式内核上使用来屏蔽数据免受中断处理程序的影响,那么您会死锁(在单处理器机器上)。
如果中断处理程序在其他内核代码持有锁时运行,它将永远旋转,因为常规内核代码无法恢复并释放锁。
只有当锁持有者可以一直运行到完成时,才能使用自旋锁。
中断处理程序可能需要锁定的解决方案是使用spin_lock_irqsave()
,它在持有自旋锁时禁用中断。使用 1 个 cpu,没有中断处理程序可以运行,因此不会出现死锁。在 smp 上,一个中断处理程序可能会开始在另一个 cpu 上旋转,但是由于持有锁的 cpu 不能被中断,所以锁最终会被释放。
引自Jonathan Corbet、Alessandro Rubini 和 Greg Kroah-Hartman的«Linux Device Drivers» :
如果一个非抢占式单处理器系统曾经在锁上自旋,它将永远自旋;没有其他线程能够获得 CPU 来释放锁(因为它不能让步)。因此,未启用抢占的单处理器系统上的自旋锁操作被优化为什么都不做,除了那些改变 IRQ 屏蔽状态的操作(在 Linux 中,这将是
spin_lock_irqsave()
)。由于抢占,即使您从未期望您的代码在 SMP 系统上运行,您仍然需要实现适当的锁定。
如果您对在中断上下文(硬件或软件)中运行的代码可以采用的自旋锁感兴趣,则必须使用一种spin_lock_*
禁用中断的形式。当您进入临界区时,一旦中断到达,不这样做会使系统死锁。
根据定义,如果您使用的是非抢占式内核,您将不会被抢占。如果您自己进行多任务处理,那不是内核的问题;那是你的问题。中断处理程序可能仍会被执行,但它们不会导致上下文切换。