參考:1、《Linux Kernel Development》3ed_CN p131-p140
2、2.6.34
單核:
//鎖的數據類型實現
typedef struct { } arch_spinlock_t; typedef struct raw_spinlock { arch_spinlock_t raw_lock; }raw_spinlock_t; typedef struct spinlock { union { struct raw_spinlock rlock; }; //以我對C的了解,這種定義方式還是第一次見到(以前見到,也沒留意過),這個union的聯合體的最后竟然沒有變量名稱,即union {xxxxx} var_name; gcc后指定為c89標准也能正常編譯與運行
//還有就是可以通過&lock->rlock,直接獲得訪問rlock的地址, 原來都是通過&lock->var_name.rlock來操作完成
//此處加個變量名,反而顯得累贅,而且如果加上變量明就必須通過變量名訪問。 }spinlock_t; //鎖是空的
//spin_lock的實現
#define __acquire(x) (void)0 #define __LOCK(lock) \ do {preempt_disable(); __acquire(lock); (void)(lock); } while(0) #define _raw_spin_lock(lock) __LOCK(lock) #define raw_spin_lock(lock) _raw_spin_lock(lock) static inline void spin_lock(spinlock_t *lock) { raw_spin_lock(&lock->rlock); }
//單核是否支持搶占在鎖上的區別:
#ifdef CONFIG_PREEMPT #define preempt_disable() \ do{ \ inc_preempt_count(); \ barrier(); \ } while(0) #else #define preempt_disable() do { } while(0)
多核:
//鎖的數據類型實現
typedef struct { volatile unsigned int lock; } arch_spinlock_t; typedef struct raw_spinlock { arch_spinlock_t raw_lock; } raw_spinlock_t; typedef struct spinlock { union { struct raw_spinlock rlock; }; } spinlock_t;
//spin_lock的實現
static inline void arch_spin_lock(arch_spinlock_t *lock) { unsigned long tmp; __asm__ __volatile__( "1: ldrex %0, [%1] \n\t" " teq %0, #0 \n\t" " strexeq %0, %2, [%1] \n\t" " teqeq %0, #0 \n\t" " bne 1b " : "=&r" (tmp) : "r" (&lock->lock), "r"(1) : "cc" smp_mb(); } static inline void do_raw_spin_lock(raw_spinlock_t *lock) { arch_spin_lock(&lock->raw_lock); } static inline void __raw_spin_lock(raw_spinlock_t *lock) { preempt_disable(); do_raw_spin_lock(lock); } static inline void spin_lock(spinlock_t *lock) { raw_spin_lock(&lock->rlock); }
附:
1、同步,鎖的問題,我認為發生在(1)進程與進程之間; (2)中斷與進程之間; (3)中斷與中斷之間(細分上半部、下半部)
(1)如果進程上下文核一個下半部共享數據,在訪問這些數據之前,需要禁止下半部的處理並得到鎖的使用權。做這些是為了本地和SMP的保護並且防止死鎖的出現。(p_128) (2)如果中斷上下文和一個下半部共享數據,在訪問數據之前,需要禁止禁止中斷並得到鎖的使用權。做這些是為了本地和SMP的保護並且防止死鎖的出現。(p_128) (3)如若在一段內核代碼操作某資源的時候系統產生了一個中斷,而該中斷的處理程序還要訪問這一個資源,這就是一個bug。(p_135最后) 關於(3),經討論使用出使用spin_lock_irqsave; 其它的也應有拌飯解決,以后會講明。
2、記下書中提出的幾條建議:
(1)最開始設計的時候就要考慮加入鎖,而不是事后才想到。如果代碼已經寫好,再在其中找到需要上鎖的部分並向其中追加鎖,是非常困難的,結果也往往也不盡如人意。避免這種亡羊補牢的做法是:在編寫代碼的開始階段就要設計恰當的鎖。(p_136中間) (2)lock contention(鎖的爭用):是指當鎖正在被占用時,有其它線程試圖獲得該鎖。鎖處於高度爭用的狀態是指有多個其他線程在等待獲得該鎖。(p_138最后) (3)被保護數據的規模描述了鎖的粒度,細粒度的鎖保護小塊數據,過粗的鎖保護大塊數據。(p_139中間) 當鎖爭用嚴重時,加鎖太粗會降低可擴展性;而鎖爭用不明顯時,加鎖過細會加大系統開銷,帶來浪費,這兩種情況都會造成系統性能下降。(p_139最后)
還是談下spin_unlock,不然總缺了什么。
單核上:
static inline void spin_unlock(spinlock_t *lock) { raw_spin_unlock(&lock->rlock); } #define raw_spin_unlock(lock) _raw_spin_unlock(lock) #define _raw_spin_unlock(lock) __UNLOCK(lock) #define __UNLOCK(lock) \ do { preempt_enable(); __release(lock); (void)(lock); } while (0) #ifdef CONFIG_PREEMPT #define preempt_enable_no_resched() \ do { \ barrier(); \ dec_preempt_count(); \ } while (0) #define preempt_check_resched() \ do { \ if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ preempt_schedule(); \ } while (0) //可能觸發內核態搶占 #define preempt_enable() \ do { \ preempt_enable_no_resched(); \ barrier(); \ preempt_check_resched(); \ } while (0) #else #define preempt_enable() do { } while (0) #endif
多核:
static inline void spin_unlock(spinlock_t *lock) { raw_spin_unlock(&lock->rlock); } #define raw_spin_unlock(lock) _raw_spin_unlock(lock) #define _raw_spin_unlock(lock) __raw_spin_unlock(lock) static inline void dsb_sev(void) { __asm__ __volatile__ ( "dsb\n" "sev" ); } static inline void arch_spin_unlock(arch_spinlock_t *lock) { smp_mb(); __asm__ __volatile__( " str %1, [%0]\n" : : "r" (&lock->lock), "r" (0) : "cc"); dsb_sev(); } void do_raw_spin_unlock(raw_spinlock_t *lock) { // debug_spin_unlock(lock); arch_spin_unlock(&lock->raw_lock); } static inline void __raw_spin_unlock(raw_spinlock_t *lock) { // spin_release(&lock->dep_map, 1, _RET_IP_); do_raw_spin_unlock(lock); preempt_enable(); //與單核搶占情形相同 }