收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
在Linux内核中何时使用spin_lock,何时使用spin_lock_irqsave很容易混淆。首先看一下代码是如何实现的。
- spin_lock
spin_lock -----> raw_spin_lock
1 static inline void __raw_spin_lock(raw_spinlock_t *lock)
2 {
3 preempt\_disable();
4 spin_acquire(&lock->dep_map, 0, 0, \_RET\_IP\_);
5 LOCK_CONTENDED(lock, do\_raw\_spin\_trylock, do\_raw\_spin\_lock);
6 }
-
只禁止内核抢占,不会关闭本地中断
-
为何需要关闭内核抢占:假如进程A获得spin_lock->进程B抢占进程A->进程B尝试获取spin_lock->由于进程B优先级比进程A高,先于A运行,而进程B又需要A unlock才得以运行,这样死锁。所以这里需要关闭抢占。 这个原理RTOS的mutex/semaphore是否相同?
a. 因为ThreadX的semaphore,假如进程B获取sema失败,会一直等待,直到A进程释放,不会死锁。
b. Mutex: mutex获取一旦失败,进程会进入sleep,直到其他进程释放;而spin_lock则不同,会一直轮训访问,且直到时间片耗完。
- spin_lock_irq
spin_lock_irq------> raw_spin_lock_irq
1 static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
2 {
3 local\_irq\_disable();
4 preempt\_disable();
5 spin_acquire(&lock->dep_map, 0, 0, \_RET\_IP\_);
6 LOCK_CONTENDED(lock, do\_raw\_spin\_trylock, do\_raw\_spin\_lock);
7 }
-
禁止内核抢占,且关闭本地中断
-
那么在spin_lock中关闭了内核抢占,不关闭中断会出现什么情况呢?假如中断中也想获得这个锁,会出现和spin_lock中举得例子相同。所以这个时候,在进程A获取lock之后,使用spin_lock_irq将中断禁止,就不会出现死锁的情况
-
在任何情况下使用spin_lock_irq都是安全的。因为它既禁止本地中断,又禁止内核抢占。
-
spin_lock比spin_lock_irq速度快,但是它并不是任何情况下都是安全的。
- spin_lock_irqsave
spin_lock_irqsave------>__raw_spin_lock_irqsave
1 static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
2 {
3 unsigned long flags;
4
5 local\_irq\_save(flags);
6 preempt\_disable();
7 spin_acquire(&lock->dep_map, 0, 0, \_RET\_IP\_);
8 /\*
9 \* On lockdep we dont want the hand-coded irq-enable of
10 \* do\_raw\_spin\_lock\_flags() code, because lockdep assumes
11 \* that interrupts are not re-enabled during lock-acquire:
12 \*/
13 #ifdef CONFIG\_LOCKDEP
14 LOCK_CONTENDED(lock, do\_raw\_spin\_trylock, do\_raw\_spin\_lock);
15 #else
16 do_raw_spin_lock_flags(lock, &flags);
17 #endif
18 return flags;
19 }
-
禁止内核抢占,关闭中断,保存中断状态寄存器的标志位
-
spin_lock_irqsave在锁返回时,之前开的中断,之后也是开的;之前关,之后也是关。但是spin_lock_irq则不管之前的开还是关,返回时都是开的(?)。
-
spin_lock_irq在自旋的时候,不会保存当前的中断标志寄存器,只会在自旋结束后,将之前的中断打开。
1. spin_lock/spin_unlock:
进程A中调用了spin_lock(&lock)然后进入临界区,此时来了一个中断(interrupt),该中断也运行在和进程A相同的CPU上,并且在该中断处理程序中恰巧也会spin_lock(&lock), 试图获取同一个锁。由于是在同一个CPU上被中断,进程A会被设置为TASK_INTERRUPT状态,中断处理程序无法获得锁,会不停的忙等,由于进程A被设置为中断状态,schedule()进程调度就无法再调度进程A运行,这样就导致了死锁!
但是如果该中断处理程序运行在不同的CPU上就不会触发死锁。 因为在不同的CPU上出现中断不会导致进程A的状态被设为TASK_INTERRUPT,只是换出。当中断处理程序忙等被换出后,进程A还是有机会获得CPU,执行并退出临界区。所以在使用spin_lock时要明确知道该锁不会在中断处理程序中使用。
2. spin_lock_irq/spin_unlock_irq
spin_lock_irq----->raw_spin_lock_irq
spin_lock_irq 和 spin_unlock_irq, 如果你确定在获取锁之前本地中断是开启的,那么就不需要保存中断状态,解锁的时候直接将本地中断启用就可以啦
3. spin_lock_irqsave/spin_unlock_irqrestore
使用spin_lock_irqsave在于你不期望在离开临界区后,改变中断的开启/关闭状态!进入临界区是关闭的,离开后它同样应该是关闭的!
如果自旋锁在中断处理函数中被用到,那么在获取该锁之前需要关闭本地中断,spin_lock_irqsave 只是下列动作的一个便利接口:
1 保存本地中断状态(这里的本地即当前的cpu的所有中断)
2 关闭本地中断
3 获取自旋锁
解锁时通过 spin_unlock_irqrestore完成释放锁、恢复本地中断到之前的状态等工作
参考微博:
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
618679757)**
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!