04、自旋锁死锁情况
一、什么是死锁?
死锁就是多个任务互相等着对方先放手,结果谁也动不了,卡住啦。这种情况程序自己没法解决,只能重启或者找人帮忙强制结束才能继续运行。比如说,两个任务互相等着对方先放手,结果谁也动不了,卡住啦。这种情况下程序自己没法解决,只能重启或者找人帮忙强制结束才能继续运行。
二、spin_lock发生死锁情况1:spin_lock
spin_lock的作用是让系统在需要时快速安全地访问共享资源。它的工作方式是:当某个CPU正在使用资源时,会暂时禁止其他任务打断当前进程。但这里有个问题:如果此时突然有硬件中断(比如网络数据到达),触发了中断处理程序,而这个处理程序也需要访问同一资源,就会出问题。
具体来说,此时当前进程已经占用了资源锁,中断处理程序只能等待锁被释放。但因为进程被Spin Lock禁止切换,必须等它主动让出CPU才能继续执行——而进程又在等待中断处理完成(比如等待数据处理完毕),这就形成了僵局:进程卡在等待中断,中断卡在等待锁,双方互相等待无法继续。这种互相等待的情况就叫做死锁。
当多个任务需要同时访问同一段关键代码时,我们会用自旋锁来保护这段区域。如果此时既有程序在运行,又有硬件中断请求,就需要先关闭CPU的中断功能。这样在锁定资源后,就不会被突然到来的中断打断,确保这段代码不会被其他任务抢占。
这就是为什么需要使用spin_lock_irq这个函数——它同时完成了"关闭中断"和"加锁"两个操作。不过这样做有两个缺点:一是系统处理其他请求的速度会变慢,因为中断被暂时关闭了;二是多了一步操作流程,可能会让程序运行得稍微慢一点。
三、发生死锁情况2:spin_lock_irq
以下是更直白的版本:
当一个进程先执行spin_lock_irq(&lock1)
时,会直接关掉中断并占用锁1。接着如果再用spin_lock_irq(&lock2)
占用锁2,此时中断仍然处于关闭状态。等释放锁2时,执行spin_unlock_irq()
会强行打开中断,但此时还没释放锁1,代码还在锁1的保护区域里运行。
这时问题就来了:如果刚打开中断就有硬件中断触发,中断处理程序可能又要占用锁1。但当前进程还拿着锁1不放,中断程序只能傻等,最后两边都卡住——这就形成了死锁。因为释放锁2时强行开中断,让中断有机会运行,却没保证锁的安全性。
而spin_lock_irqsave
就不会这样。它在占用电锁时会记住中断原本是开还是关的(比如用一个变量保存状态),等释放锁时只恢复最初的中断状态,而不是强行开中断。这样既避免了中途意外打开中断导致的死锁,又能确保锁的正确使用。不过这种方法需要多存一次状态,速度会比简单版稍微慢一点。
简单总结:直接用spin_lock_irq
可能因中途开中断引发死锁,而spin_lock_irqsave
通过记录状态更安全,但会慢一点点。
四、总结
- spin_lock 不推荐使用。它可能导致程序卡死(死锁),所以一般不用。 仅在需要极高的实时性时才考虑用它(比如必须立刻完成操作,不能等待)。
- spin_lock_irq 和
spin_lock
类似,但会关闭所有中断(连硬件中断也会被暂停)。 同样可能死锁,所以也不安全。 仅在绝对需要极高实时性时使用(比如实时性比中断响应更重要)。 - spin_lock_irqsave 最安全的选择!它会记录中断状态,用完后自动恢复中断。 虽然实时性稍差(因为需要保存状态),但能避免死锁问题,是推荐优先使用的方案。
如果不确定资源是否会被中断或抢占,直接用 spin_lock_irqsave
。它虽然稍微慢一点,但能避免很多问题。