BKL(Big Kernel Lock)
BKL即全局內(nèi)核鎖,也稱(chēng)大內(nèi)核鎖,它是一個(gè)全局自旋鎖。大內(nèi)核鎖也是用來(lái)保護(hù)臨界區(qū)資源的,避免出現(xiàn)多個(gè)處理器上的進(jìn)程同時(shí)訪問(wèn)同一區(qū)域,整個(gè)內(nèi)核中只有一個(gè)大內(nèi)核鎖。
BKL是一個(gè)名為kernel_flag的自旋鎖,持有該鎖的進(jìn)程仍可以睡眠,當(dāng)睡眠時(shí)持有的鎖將被自動(dòng)釋放,該進(jìn)程被喚醒時(shí)重新持有該鎖。Linux允許一個(gè)進(jìn)程可以遞歸的持有BKL,BKL是一個(gè)遞歸鎖。
它的設(shè)計(jì)思想是,一旦某個(gè)內(nèi)核路徑獲取了這把鎖,那么其他所有的內(nèi)核路徑都不能再獲取到這把鎖。自旋鎖加鎖的對(duì)象一般是一個(gè)全局變量,大內(nèi)核鎖加鎖的對(duì)象是一段代碼,里面可能包含多個(gè)全局變量。那么他帶來(lái)的問(wèn)題是,雖然A只需要互斥訪問(wèn)全局變量a,但附帶鎖了全局變量b,從而導(dǎo)致B不能訪問(wèn)b了
mutex(互斥鎖)
/linux/include/linux/mutex.h
47struct mutex {
48
49 atomic_t count;
50 spinlock_t wait_lock;
51 struct list_head wait_list;
52#ifdef CONFIG_DEBUG_MUTEXES
53 struct thread_info *owner;
54 const char *name;
55 void *magic;
56#endif
57#ifdef CONFIG_DEBUG_LOCK_ALLOC
58 struct lockdep_map dep_map;
59#endif
60};
一、作用及訪問(wèn)規(guī)則:
互斥鎖主要用于實(shí)現(xiàn)內(nèi)核中的互斥訪問(wèn)功能。內(nèi)核互斥鎖是在原子API之上實(shí)現(xiàn)的,但這對(duì)于內(nèi)核用戶(hù)是不可見(jiàn)的。對(duì)它的訪問(wèn)必須遵循一些規(guī)則:同一時(shí)間只能有一個(gè)任務(wù)持有互斥鎖,而且只有這個(gè)任務(wù)可以對(duì)互斥鎖進(jìn)行解鎖?;コ怄i不能進(jìn)行遞歸鎖定或解鎖。一個(gè)互斥鎖對(duì)象必須通過(guò)其API初始化,而不能使用memset或復(fù)制初始化。一個(gè)任務(wù)在持有互斥鎖的時(shí)候是不能結(jié)束的?;コ怄i所使用的內(nèi)存區(qū)域是不能被釋放的。使用中的互斥鎖是不能被重新初始化的。并且互斥鎖不能用于中斷上下文。但是互斥鎖比當(dāng)前的內(nèi)核信號(hào)量選項(xiàng)更快,并且更加緊湊,因此如果它們滿足您的需求,那么它們將是您明智的選擇。
二、各字段詳解:
1、atomic_t count; --指示互斥鎖的狀態(tài):1沒(méi)有上鎖,可以獲得;0被鎖定,不能獲得;負(fù)數(shù)被鎖定,且可能在該鎖上有等待進(jìn)程初始化為沒(méi)有上鎖。
2、spinlock_t wait_lock;--等待獲取互斥鎖中使用的自旋鎖。在獲取互斥鎖的過(guò)程中,操作會(huì)在自旋鎖的保護(hù)中進(jìn)行。初始化為為鎖定。
3、struct list_head wait_list;--等待互斥鎖的進(jìn)程隊(duì)列。
四、操作:
1、定義并初始化:
struct mutex mutex;
mutex_init(&mutex);
79# define mutex_init(mutex) \
80do { \
81 static struct lock_class_key __key; \
82 \
83 __mutex_init((mutex), #mutex, &__key); \
84} while (0)
42void
43__mutex_init(struct mutex *lock, const char *name, structlock_class_key *key)
44{
45 atomic_set(&lock-》count, 1);
46 spin_lock_init(&lock-》wait_lock);
47 INIT_LIST_HEAD(&lock-》wait_list);
48
49 debug_mutex_init(lock, name, key);
50}
直接定于互斥鎖mutex并初始化為未鎖定,己count為1,wait_lock為未上鎖,等待隊(duì)列wait_list為空。
2、獲取互斥鎖:
?。?)具體參見(jiàn)linux/kernel/mutex.c
void inline __sched mutex_lock(struct mutex *lock)
{
might_sleep();
__mutex_fastpath_lock(&lock-》count,__mutex_lock_slowpath);
}
獲取互斥鎖。實(shí)際上是先給count做自減操作,然后使用本身的自旋鎖進(jìn)入臨界區(qū)操作。首先取得count的值,再將count置為-1,判斷如果原來(lái)count的值為1,也即互斥鎖可以獲得,則直接獲取,跳出。否則進(jìn)入循環(huán)反復(fù)測(cè)試互斥鎖的狀態(tài)。在循環(huán)中,也是先取得互斥鎖原來(lái)的狀態(tài),再將其置為-1,判斷如果可以獲?。ǖ扔?),則退出循環(huán),否則設(shè)置當(dāng)前進(jìn)程的狀態(tài)為不可中斷狀態(tài),解鎖自身的自旋鎖,進(jìn)入睡眠狀態(tài),待被在調(diào)度喚醒時(shí),再獲得自身的自旋鎖,進(jìn)入新一次的查詢(xún)其自身狀態(tài)(該互斥鎖的狀態(tài))的循環(huán)。
?。?)具體參見(jiàn)linux/kernel/mutex.c
int __sched mutex_lock_interruptible(struct mutex *lock)
{
might_sleep();
return __mutex_fastpath_lock_retval(&lock-》count,__mutex_lock_interruptible_slowpath);
}
和mutex_lock()一樣,也是獲取互斥鎖。在獲得了互斥鎖或進(jìn)入睡眠直到獲得互斥鎖之后會(huì)返回0。如果在等待獲取鎖的時(shí)候進(jìn)入睡眠狀態(tài)收到一個(gè)信號(hào)(被信號(hào)打斷睡眠),則返回_EINIR。
?。?)具體參見(jiàn)linux/kernel/mutex.c
int __sched mutex_trylock(struct mutex *lock)
{
return __mutex_fastpath_trylock(&lock-》count,
__mutex_trylock_slowpath);
}
試圖獲取互斥鎖,如果成功獲取則返回1,否則返回0,不等待。
3、釋放互斥鎖:
具體參見(jiàn)linux/kernel/mutex.c
void __sched mutex_unlock(struct mutex *lock)
{
__mutex_fastpath_unlock(&lock-》count,__mutex_unlock_slowpath);
}
釋放被當(dāng)前進(jìn)程獲取的互斥鎖。該函數(shù)不能用在中斷上下文中,而且不允許去釋放一個(gè)沒(méi)有上鎖的互斥鎖。
4.void mutex_destroy(struct mutex *lock) --清除互斥鎖,使互斥鎖不可用
用mutex_destroy()函數(shù)解除由lock指向的互斥鎖的任何狀態(tài)。在調(diào)用執(zhí)行這個(gè)函數(shù)的時(shí)候,lock指向的互斥鎖不能在被鎖狀態(tài)。儲(chǔ)存互斥鎖的內(nèi)存不被釋放。
返回值--mutex_destroy()在成功執(zhí)行后返回零。其他值意味著錯(cuò)誤。在以下情況發(fā)生時(shí),函數(shù)失敗并返回相關(guān)值。
EINVAL 非法參數(shù)
EFAULT mp指向一個(gè)非法地址。
5.static inline int mutex_is_locked(struct mutex *lock)--測(cè)試互斥鎖的狀態(tài)
這個(gè)調(diào)用實(shí)際上編譯成一個(gè)內(nèi)聯(lián)函數(shù)。如果互斥鎖被持有(鎖定),那么就會(huì)返回1;否則,返回0。
五、使用形式:
struct mutex mutex;
mutex_init(&mutex);
。。。
mutex_lock(&mutex);
。。。
mutex_unlock(&mutex);
順序鎖
順序鎖為寫(xiě)者賦予更高的優(yōu)先級(jí),寫(xiě)者永遠(yuǎn)不會(huì)等待讀者。缺點(diǎn)是讀者有時(shí)不得不讀多次數(shù)據(jù)以獲取正確的結(jié)果。
順序鎖的數(shù)據(jù)結(jié)構(gòu)中除了有spinlock外,還有一個(gè)順序號(hào)。如果成功獲得鎖,順序鎖的順序號(hào)會(huì)加1,以便讀者能夠檢查出是否在讀期間有寫(xiě)者訪問(wèn)過(guò)。讀者在讀取數(shù)據(jù)前后兩次讀順序值,如果兩次值不相同,則說(shuō)明讀取期間有新的寫(xiě)者操作過(guò)數(shù)據(jù)了,那么本次讀取就是無(wú)效的。
典型使用:
讀端:
do {
seqnum = read_seqbegin(&seqlock_a);
//讀操作代碼塊
。。.
} while (read_seqretry(&seqlock_a, seqnum));
寫(xiě)端:
spin_lock(&lock);
write_seqlock(&seqlock_a)
。。.
write_sequnlock(&seqlock_a)
spin_unlock(&lock);
寫(xiě)者通過(guò)調(diào)用write_seqlock()和write_sequnlock()獲取和釋放順序鎖。write_seqlock()函數(shù)獲取seqlock_t數(shù)據(jù)結(jié)構(gòu)中的自旋鎖,然后使順序計(jì)數(shù)器sequence加1;write_sequnlock()函數(shù)再次增加順序計(jì)數(shù)器sequence,然后釋放自旋鎖。這樣可以保證寫(xiě)者在整個(gè)寫(xiě)的過(guò)程中,計(jì)數(shù)器sequence的值是奇數(shù),并且當(dāng)沒(méi)有寫(xiě)者在改變數(shù)據(jù)的時(shí)候,計(jì)數(shù)器的值是偶數(shù)。
read_seqbegin()返回順序鎖的當(dāng)前順序號(hào);如果局部變量seq的值是奇數(shù)(寫(xiě)者在read_seqbegin()函數(shù)被調(diào)用后,正更新數(shù)據(jù)結(jié)構(gòu)),或seq的值與順序鎖的順序計(jì)數(shù)器的當(dāng)前值不匹配(當(dāng)讀者正執(zhí)行臨界區(qū)代碼時(shí),寫(xiě)者開(kāi)始工作),read_seqretry()就返回1,說(shuō)明本次讀取失敗,需要重新讀取 。
并不是每一種資源都可以使用順序鎖來(lái)保護(hù)。一般來(lái)說(shuō),必須在滿足下述條件時(shí)才能使用順序鎖:
1. 讀者的臨界區(qū)資源不包括被寫(xiě)者修改和被讀者取值的指針,否則,寫(xiě)者有可能使指針失效,讀者讀取時(shí)會(huì)產(chǎn)生OPPs。
2. 讀者的臨界區(qū)代碼沒(méi)有副作用。
何時(shí)使用順序鎖?
讀操作遠(yuǎn)多于寫(xiě)操作、且寫(xiě)操作很緊急時(shí)使用順序鎖。
評(píng)論