我在一个拥有1个CPU且非预先存在的Linux内核(2.6.x)的系统上看到,一个spin_lock调用相当于一个空调用,因此以这种方式实现。
我无法理解:它不应该相当于在互斥上睡一觉吗?即使在非preemtive内核上,中断处理程序仍然可能被执行,或者我可能会调用一个将原始线程置于休眠状态的函数。因此,如果将空的spin_lock调用实现为互斥锁,那么它就是“安全”的。
有没有我得不到的东西?
答案 0 :(得分:6)
如果你在非抢占式内核上使用spin_lock()
来屏蔽数据与中断处理程序,那么你就会死锁(在单处理器机器上)。
如果中断处理程序运行而其他内核代码持有锁,它将永远旋转,因为常规内核代码无法恢复并释放锁。
只有在锁定夹持器可以始终运行完毕时才能使用自旋锁。
中断处理程序可能需要的锁的解决方案是使用spin_lock_irqsave()
,它会在保持自旋锁时禁用中断。使用1个cpu,没有中断处理程序可以运行,因此不会出现死锁。在smp上,中断处理程序可能会在另一个cpu上开始旋转,但由于持有锁的cpu不能被中断,锁最终会被释放。
答案 1 :(得分:6)
回答问题的两个部分:
即使在非内存内核上,中断处理程序仍然可以执行,例如......
spin_lock()
不应该防止中断处理程序 - 只有用户上下文内核代码。 spin_lock_irqsave()
是中断禁用版本,此不是非抢占式单处理器上的无操作。
...或者我可能会调用一个让原始线程进入睡眠状态的函数。
持有旋转锁时不允许睡觉。这是“原子调度”错误。如果你想睡觉,你必须使用互斥量(再次 - 这些不是非抢占式单处理器上的无操作)。
答案 2 :(得分:5)
来自«Linux设备驱动程序»,来自Jonathan Corbet,Alessandro Rubini和Greg Kroah-Hartman:
如果一个非抢占式单处理器系统曾经进入过旋转 锁,它会永远旋转;没有其他线程可以 获取CPU以释放锁(因为它无法屈服)。 正因为如此,单片机系统上的自旋锁操作没有 启用抢占被优化为什么也不做,除了 改变IRQ掩码状态的那些(在Linux中,那将是
spin_lock_irqsave()
)。因为先发制人,即使你永远不会 希望您的代码在SMP系统上运行,您仍然需要实现 正确锁定。
如果您对在中断上下文(硬件或软件)中运行的代码可以使用的自旋锁感兴趣,则必须使用禁用中断的spin_lock_*
形式。如果在您进入关键部分时中断到达,则不会这样做会使系统死锁。
答案 3 :(得分:1)
根据定义,如果您使用的是非抢占式内核,则不会被抢占。如果你做自己的多任务处理,那不是内核的问题;那是你的问题。中断处理程序仍然可以执行,但它们不会导致上下文切换。