引言
今天談?wù)?a href="http://ttokpm.com/v/tag/538/" target="_blank">linux中常見(jiàn)并發(fā)訪問(wèn)的保護(hù)機(jī)制設(shè)計(jì)原理。為什么要寫這篇文章呢?其實(shí)想幫助自己及讀者更深入的了解背后的原理(據(jù)可靠消息,鎖的實(shí)現(xiàn)經(jīng)常出現(xiàn)在筆試環(huán)節(jié)。既可以考察面試者對(duì)鎖的原理的理解,又可以考察面試者編程技能)。我們拋開linux中匯編代碼。用C語(yǔ)言為大家呈現(xiàn)背后實(shí)現(xiàn)的原理。同時(shí),文章中的代碼都沒(méi)有考慮并發(fā)情況(例如某些操作需要原子性,或者數(shù)據(jù)需要保護(hù)等)。
注:部分代碼都是根據(jù)ARM64架構(gòu)匯編代碼翻譯成C語(yǔ)言并經(jīng)過(guò)精簡(jiǎn)(例如:spin lock、read-write lock)。也有部分代碼實(shí)現(xiàn)是為了呈現(xiàn)背后設(shè)計(jì)的原理自己編寫的,而不是精簡(jiǎn)linux中實(shí)現(xiàn)的代碼(例如mutex)。
自旋鎖(spin lock)
自旋鎖是linux中使用非常頻繁的鎖,原理簡(jiǎn)單。當(dāng)進(jìn)程A申請(qǐng)鎖成功后,進(jìn)程B申請(qǐng)鎖就會(huì)失敗,但是不會(huì)調(diào)度,原地自旋。就在原地轉(zhuǎn)到天昏地暗只為等到進(jìn)程A釋放鎖。由于不會(huì)睡眠和調(diào)度的特性,在中斷上下文中,數(shù)據(jù)的保護(hù)一般都是選擇自旋鎖。如果有多個(gè)進(jìn)程去申請(qǐng)鎖。當(dāng)?shù)谝粋€(gè)申請(qǐng)鎖成功的線程在釋放的時(shí)候,其他進(jìn)程是競(jìng)爭(zhēng)的關(guān)系。因此是一種不公平。所以現(xiàn)在的linux采用的是排隊(duì)機(jī)制。先到先得。誰(shuí)先申請(qǐng),誰(shuí)就先得到鎖。
原理
舉個(gè)例子,大家應(yīng)該都去過(guò)銀行辦業(yè)務(wù)吧。銀行的辦事大廳一般會(huì)有幾個(gè)窗口同步進(jìn)行。今天很不巧,只有一個(gè)窗口提供服務(wù)?,F(xiàn)在的銀行服務(wù)都是采用取號(hào)排隊(duì),叫號(hào)服務(wù)的方式。當(dāng)你去銀行辦理業(yè)務(wù)的時(shí)候,首先會(huì)去取號(hào)機(jī)器領(lǐng)取小票,上面寫著你排多少號(hào)。然后你就可以排隊(duì)等待了。一般還會(huì)有個(gè)顯示屏,上面會(huì)顯示一個(gè)數(shù)字(例如:"請(qǐng)xxx號(hào)到1號(hào)窗口辦理"),代表當(dāng)前可以被服務(wù)顧客的排隊(duì)號(hào)碼。每辦理完一個(gè)顧客的業(yè)務(wù),顯示屏上面的數(shù)字都會(huì)增加1。等待的顧客都會(huì)對(duì)比自己手上寫的編號(hào)和顯示屏上面是否一致,如果一致的話,就可以去前臺(tái)辦理業(yè)務(wù)了?,F(xiàn)在早上剛開業(yè),顧客A是今天的第一個(gè)顧客,去取號(hào)機(jī)器領(lǐng)取0號(hào)(next計(jì)數(shù))小票,然后看到顯示屏上顯示0(owner計(jì)數(shù)),顧客A就知道現(xiàn)在輪到自己辦理業(yè)務(wù)了。顧客A到前臺(tái)辦理業(yè)務(wù)(持有鎖)中,顧客B來(lái)了。同樣,顧客B去取號(hào)機(jī)器拿到1號(hào)(next計(jì)數(shù))小票。然后乖乖的坐在旁邊等候。顧客A依然在辦理業(yè)務(wù)中,此時(shí)顧客C也來(lái)了。顧客C去取號(hào)機(jī)器拿到2號(hào)(next計(jì)數(shù))小票。顧客C也乖乖的找個(gè)座位繼續(xù)等待。終于,顧客A的業(yè)務(wù)辦完了(釋放鎖)。然后,顯示屏上面顯示1(owner計(jì)數(shù))。顧客B和C都對(duì)比顯示屏上面的數(shù)字和自己手中小票的數(shù)字是否相等。顧客B終于可以辦理業(yè)務(wù)了(持有鎖)。顧客C依然等待中。顧客B的業(yè)務(wù)辦完了(釋放鎖)。然后,顯示屏上面顯示2(owner計(jì)數(shù))。顧客C終于開始辦理業(yè)務(wù)(持有鎖)。顧客C的業(yè)務(wù)辦完了(釋放鎖)。3個(gè)顧客都辦完了業(yè)務(wù)離開了。只留下一個(gè)銀行柜臺(tái)服務(wù)員。最終,顯示屏上面顯示3(owner計(jì)數(shù))。取號(hào)機(jī)器的下一個(gè)排隊(duì)號(hào)也是3號(hào)(next計(jì)數(shù))。無(wú)人辦理業(yè)務(wù)(鎖是釋放狀態(tài))。
linux中針對(duì)每一個(gè)spin lock會(huì)有兩個(gè)計(jì)數(shù)。分別是next和owner(初始值為0)。進(jìn)程A申請(qǐng)鎖時(shí),會(huì)判斷next和owner的值是否相等。如果相等就代表鎖可以申請(qǐng)成功,否則原地自旋。直到owner和next的值相等才會(huì)退出自旋。假設(shè)進(jìn)程A申請(qǐng)鎖成功,然后會(huì)next加1。此時(shí)owner值為0,next值為1。進(jìn)程B也申請(qǐng)鎖,保存next得值到局部變量tmp(tmp = 1)中。由于next和owner值不相等,因此原地自旋讀取owner的值,判斷owner和tmp是否相等,直到相等退出自旋狀態(tài)。當(dāng)然next的值還是加1,變成2。進(jìn)程A釋放鎖,此時(shí)會(huì)將owner的值加1,那么此時(shí)B進(jìn)程的owner和tmp的值都是1,因此B進(jìn)程獲得鎖。當(dāng)B進(jìn)程釋放鎖后,同樣會(huì)將owner的值加1。最后owner和next都等于2,代表沒(méi)有進(jìn)程持有鎖。next就是一個(gè)記錄申請(qǐng)鎖的次數(shù),而owner是持有鎖進(jìn)程的計(jì)數(shù)值。
實(shí)現(xiàn)
我們首先定義描述自旋鎖的結(jié)構(gòu)體arch_spinlock_t。
typedef struct {
union {
unsigned int slock;
struct __raw_tickets {
unsigned short owner;
unsigned short next;
} tickets;
};
} arch_spinlock_t;
如上面的原理描述,我們需要兩個(gè)計(jì)數(shù),分別是owner和next。slock所占內(nèi)存區(qū)域覆蓋owner和next(據(jù)說(shuō)C語(yǔ)言學(xué)好的都能看得懂)。下面實(shí)現(xiàn)申請(qǐng)鎖操作 arch_spin_lock。
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
arch_spinlock_t old_lock;
old_lock.slock = lock->slock; /* 1 */
lock->tickets.next++; /* 2 */
while (old_lock.tickets.next != old_lock.tickets.owner) { /* 3 */
wfe(); /* 4 */
old_lock.tickets.owner = lock->tickets.owner; /* 5 */
}
}
繼續(xù)上面的舉例。顧客從取號(hào)機(jī)器得到排隊(duì)號(hào)。
取號(hào)機(jī)器更新下個(gè)顧客將要拿到的排隊(duì)號(hào)。
看一下顯示屏,判斷是否輪到自己了。
wfe()函數(shù)是指ARM64架構(gòu)的WFE(wait for event)匯編指令。WFE是讓ARM核進(jìn)入低功耗模式的指令。當(dāng)進(jìn)程拿不到鎖的時(shí)候,原地自旋不如cpu睡眠。節(jié)能。睡下去之后,什么時(shí)候醒來(lái)呢?就是等到持有鎖的進(jìn)程釋放的時(shí)候,醒過(guò)來(lái)判斷是否可以持有鎖。如果不能獲得鎖,繼續(xù)睡眠即可。這里就相當(dāng)于顧客先小憩一會(huì),等到廣播下一位排隊(duì)者的時(shí)候,醒來(lái)看看是不是自己。
前臺(tái)已經(jīng)為上一個(gè)顧客辦理完成業(yè)務(wù),剩下排隊(duì)的顧客都要抬頭看一下顯示屏是不是輪到自己了。
釋放鎖的操作就非常簡(jiǎn)單了。還記得上面銀行辦理業(yè)務(wù)的例子嗎?釋放鎖的操作僅僅是顯示屏上面的排隊(duì)號(hào)加1。我們僅僅需要將owner計(jì)數(shù)加1即可。arch_spin_unlock實(shí)現(xiàn)如下。
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
lock->tickets.owner++;
sev();
}
sev()函數(shù)是指ARM64架構(gòu)的SEV匯編指令。當(dāng)進(jìn)程無(wú)法獲取鎖的時(shí)候會(huì)使用WFE指令使CPU睡眠?,F(xiàn)在釋放鎖了,自然要喚醒所有睡眠的CPU醒來(lái)檢查自己是不是可以獲取鎖。
信號(hào)量(semaphore)
信號(hào)量(semaphore)是進(jìn)程間通信處理同步互斥的機(jī)制。是在多線程環(huán)境下使用的一種措施,它負(fù)責(zé)協(xié)調(diào)各個(gè)進(jìn)程,以保證他們能夠正確、合理的使用公共資源。 它和spin lock最大的不同之處就是:無(wú)法獲取信號(hào)量的進(jìn)程可以睡眠,因此會(huì)導(dǎo)致系統(tǒng)調(diào)度。
原理
信號(hào)量一般可以用來(lái)標(biāo)記可用資源的個(gè)數(shù)。老規(guī)矩,還是舉個(gè)例子。假設(shè)圖書館有2本《C語(yǔ)言從入門到放棄》書籍。A同學(xué)想學(xué)C語(yǔ)言,于是發(fā)現(xiàn)這本書特別的好。于是就去學(xué)校的圖書館借書,A同學(xué)成功的從圖書館借走一本。這時(shí),A同學(xué)室友B同學(xué)發(fā)現(xiàn)A同學(xué)竟然在偷偷的學(xué)習(xí)武功秘籍(C語(yǔ)言)。于是,B同學(xué)也去借一本。此時(shí),圖書館已經(jīng)沒(méi)有書了。C同學(xué)也想借這本書,可能是這本書太火了。圖書館管理員告訴C同學(xué),圖書館這本書都被借走了。如果有同學(xué)換回來(lái),會(huì)第一時(shí)間通知你。于是,管理員就把C同學(xué)的信息登記先來(lái),以備后續(xù)通知C同學(xué)來(lái)借書。所以,C同學(xué)只能悲傷的走了(如果是自旋鎖的原理的話,那么C同學(xué)將會(huì)端個(gè)小板凳坐在圖書館,一直要等到A同學(xué)或者B同學(xué)還書并借走)。
實(shí)現(xiàn)
為了記錄可用資源的數(shù)量,我們肯定需要一個(gè)count計(jì)數(shù),標(biāo)記當(dāng)前可用資源數(shù)量。當(dāng)然還要一個(gè)可以像圖書管理員一樣的筆記本功能。用來(lái)記錄等待借書的同學(xué)。所以,一個(gè)雙向鏈表即可。因此只需要一個(gè)count計(jì)數(shù)和等待進(jìn)程的鏈表頭即可。描述信號(hào)量的結(jié)構(gòu)體如下。
struct semaphore {
unsigned int count;
struct list_head wait_list;
};
在linux中,每個(gè)進(jìn)程就相當(dāng)于是每個(gè)借書的同學(xué)。通知一個(gè)同學(xué),就相當(dāng)于喚醒這個(gè)進(jìn)程。因此,我們還需要一個(gè)結(jié)構(gòu)體記錄當(dāng)前的進(jìn)程信息(task_struct)。
struct semaphore_waiter {
struct list_head list;
struct task_struct *task;
};
struct semaphore_waiter的list成員是當(dāng)進(jìn)程無(wú)法獲取信號(hào)量的時(shí)候掛入semaphore的wait_list成員。task成員就是記錄后續(xù)被喚醒的進(jìn)程信息。
一切準(zhǔn)備就緒,現(xiàn)在就可以實(shí)現(xiàn)信號(hào)量的申請(qǐng)函數(shù)。
void down(struct semaphore *sem)
{
struct semaphore_waiter waiter;
if (sem->count > 0) {
sem->count--; /* 1 */
return;
}
waiter.task = current; /* 2 */
list_add_tail(&waiter.list, &sem->wait_list); /* 2 */
schedule(); /* 3 */
}
如果信號(hào)量標(biāo)記的資源還有剩余,自然可以成功獲取信號(hào)量。只需要遞減可用資源計(jì)數(shù)。
既然無(wú)法獲取信號(hào)量,就需要將當(dāng)前進(jìn)程掛入信號(hào)量的等待隊(duì)列鏈表上。
schedule()主要是觸發(fā)任務(wù)調(diào)度的示意函數(shù),主動(dòng)讓出CPU使用權(quán)。在讓出之前,需要將當(dāng)前進(jìn)程從運(yùn)行隊(duì)列上移除。
釋放信號(hào)的實(shí)現(xiàn)也是比較簡(jiǎn)單。實(shí)現(xiàn)如下。
void up(struct semaphore *sem)
{
struct semaphore_waiter waiter;
if (list_empty(&sem->wait_list)) {
sem->count++; /* 1 */
return;
}
waiter = list_first_entry(&sem->wait_list, struct semaphore_waiter, list);
list_del(&waiter->list); /* 2 */
wake_up_process(waiter->task); /* 2 */
}
如果等待鏈表沒(méi)有進(jìn)程,那么自然只需要增加資源計(jì)數(shù)。
從等待進(jìn)程鏈表頭取出第一個(gè)進(jìn)程,并從鏈表上移除。然后就是喚醒該進(jìn)程。
讀寫鎖(read-write lock)
不管是自旋鎖還是信號(hào)量在同一時(shí)間只能有一個(gè)進(jìn)程進(jìn)入臨界區(qū)。對(duì)于有些情況,我們是可以區(qū)分讀寫操作的。因此,我們希望對(duì)于讀操作的進(jìn)程可以并發(fā)進(jìn)行。對(duì)于寫操作只限于一個(gè)進(jìn)程進(jìn)入臨界區(qū)。而這種同步機(jī)制就是讀寫鎖。讀寫鎖一般具有以下幾種性質(zhì)。
同一時(shí)間有且僅有一個(gè)寫進(jìn)程進(jìn)入臨界區(qū)。
在沒(méi)有寫進(jìn)程進(jìn)入臨界區(qū)的時(shí)候,同時(shí)可以有多個(gè)讀進(jìn)程進(jìn)入臨界區(qū)。
讀進(jìn)程和寫進(jìn)程不可以同時(shí)進(jìn)入臨界區(qū)。
讀寫鎖有兩種,一種是信號(hào)量類型,另一種是spin lock類型。下面以spin lock類型講解。
原理
老規(guī)矩,還是舉個(gè)例子理解讀寫鎖。我絞盡腦汁才想到一個(gè)比較貼切的例子。這個(gè)例子來(lái)源于生活。我發(fā)現(xiàn)公司一般都會(huì)有保潔阿姨打掃廁所。如果以男廁所為例的話,我覺(jué)得男士進(jìn)入廁所就相當(dāng)于讀者進(jìn)入臨界區(qū)。因?yàn)榭梢杂卸鄠€(gè)男士進(jìn)廁所。而保潔阿姨進(jìn)入男士廁所就相當(dāng)于寫者進(jìn)入臨界區(qū)。假設(shè)A男士發(fā)現(xiàn)保潔阿姨不在打掃廁所,就進(jìn)入廁所。隨后B和C同時(shí)也進(jìn)入廁所。然后保潔阿姨準(zhǔn)備打掃廁所,發(fā)現(xiàn)有男士在廁所里面,因此只能在門口等待。ABC都離開了廁所。保潔阿姨迅速進(jìn)入廁所打掃。然后D男士去上廁所,發(fā)現(xiàn)保潔阿姨在里面?;伊锪锏某鰜?lái)了在門口等著?,F(xiàn)在體會(huì)到了寫者(保潔阿姨)具有排他性,讀者(男士)可以并發(fā)進(jìn)入臨界區(qū)了吧。
既然我們?cè)试S多個(gè)讀者進(jìn)入臨界區(qū),因此我們需要一個(gè)計(jì)數(shù)統(tǒng)計(jì)讀者的個(gè)數(shù)。同時(shí),由于寫者永遠(yuǎn)只存在一個(gè)進(jìn)入臨界區(qū),因此只需要一個(gè)bit標(biāo)記是否有寫進(jìn)程進(jìn)入臨界區(qū)。所以,我們可以將兩個(gè)計(jì)數(shù)合二為一。只需要1個(gè)unsigned int類型即可。最高位(bit31)代表是否有寫者進(jìn)入臨界區(qū),低31位(0~30bit)統(tǒng)計(jì)讀者個(gè)數(shù)。
+----+-------------------------------------------------+
| 31 | 30 0 |
+----+-------------------------------------------------+
| |
| +----> [0:30] Read Thread Counter
+-------------------------> [31] Write Thread Counter
實(shí)現(xiàn)
描述讀寫鎖只需要1個(gè)變量即可,因此我們可以定義讀寫鎖的結(jié)構(gòu)體如下。
typedef struct {
volatile unsigned int lock;
} arch_rwlock_t;
既然區(qū)分讀寫操作,因此肯定會(huì)有兩個(gè)申請(qǐng)鎖函數(shù),分別是讀和寫。首先,我們看一下read_lock操作的實(shí)現(xiàn)。
static inline void arch_read_lock(arch_rwlock_t *rw)
{
unsigned int tmp;
sevl(); /* 1 */
do {
wfe();
tmp = rw->lock;
tmp++; /* 2 */
} while(tmp & (1 << 31)); /* 3 */
rw->lock = tmp;
}
sevl()函數(shù)是ARM64架構(gòu)中SEVL匯編指令。SEVL和SEV的區(qū)別是,SEVL僅僅修改本地CPU的PE寄存器值,這樣下面的WFE指令第一次執(zhí)行的時(shí)候不會(huì)睡眠。
增加讀者計(jì)數(shù),最后會(huì)更新到rw->lock中。
更新rw->lock前提是沒(méi)有寫者,因此這里會(huì)判斷是否有寫者已經(jīng)進(jìn)入臨界區(qū)(判斷方法是rw->lock變量bit31的值)。如果,有寫者已經(jīng)進(jìn)入臨界區(qū),就在這里循環(huán),并WFE指令睡眠。類似上面介紹的spin lock實(shí)現(xiàn)。
當(dāng)讀進(jìn)程離開臨界區(qū)的時(shí)候會(huì)調(diào)用read_unlock釋放鎖。read_unlock實(shí)現(xiàn)如下。
static inline void arch_read_unlock(arch_rwlock_t *rw)
{
rw->lock--;
sev();
}
實(shí)現(xiàn)很簡(jiǎn)單,和spin_unlock如出一轍。遞減讀者計(jì)數(shù),然后使用SEV指令喚醒所有的CPU,檢查等待狀態(tài)的進(jìn)程是否可以獲取鎖。
讀操作看完了,我們看看寫操作是如何實(shí)現(xiàn)的。arch_write_lock實(shí)現(xiàn)如下。
static inline void arch_write_lock(arch_rwlock_t *rw)
{
unsigned int tmp;
sevl();
do {
wfe();
tmp = rw->lock;
} while(tmp); /* 1 */
rw->lock = 1 << 31; /* 2 */
}
由于寫者是排他的(讀者和寫者都不能有),因此這里只有rw->lock的值為0,當(dāng)前的寫者才可以進(jìn)入臨界區(qū)。
置位rw->lock的bit31,代表有寫者進(jìn)入臨界區(qū)。
當(dāng)寫進(jìn)程離開臨界區(qū)的時(shí)候會(huì)調(diào)用write_unlock釋放鎖。write_unlock實(shí)現(xiàn)如下。
static inline void arch_write_unlock(arch_rwlock_t *rw)
{
rw->lock = 0; /* 1 */
sev(); /* 2 */
}
同樣由于寫者是排他的,因此只需要將rw->lock置0即可。代表沒(méi)有任何進(jìn)程進(jìn)入臨界區(qū)。畢竟是因?yàn)橥粫r(shí)間只能有一個(gè)寫者進(jìn)入臨界區(qū),當(dāng)這個(gè)寫者離開臨界區(qū)的時(shí)候,肯定是意味著現(xiàn)在沒(méi)有任何進(jìn)程進(jìn)入臨界區(qū)。
使用SEV指令喚醒所有的CPU,檢查等待狀態(tài)的進(jìn)程是否可以獲取鎖。
以上的代碼實(shí)現(xiàn)其實(shí)會(huì)導(dǎo)致寫進(jìn)程餓死現(xiàn)象。例如,A、B、C三個(gè)進(jìn)程進(jìn)入讀臨界區(qū),D進(jìn)程嘗試獲得寫鎖,此時(shí)只能等待A、B、C三個(gè)進(jìn)程退出臨界區(qū)。如果在退出之前又有F、G進(jìn)程進(jìn)入讀臨界區(qū),那么將出現(xiàn)D進(jìn)程餓死現(xiàn)象。
互斥量(mutex)
前文提到的semaphore在初始化count計(jì)數(shù)的時(shí)候,可以分為計(jì)數(shù)信號(hào)量和互斥信號(hào)量(二值信號(hào)量)。mutex和初始化計(jì)數(shù)為1的二值信號(hào)量有很大的相似之處。他們都可以用做資源互斥。但是mutex卻有一個(gè)特殊的地方:只有持鎖者才能解鎖。但是,二值信號(hào)量卻可以在一個(gè)進(jìn)程中獲取信號(hào)量,在另一個(gè)進(jìn)程中釋放信號(hào)量。如果是應(yīng)用在嵌入式應(yīng)用的RTOS,針對(duì)mutex的實(shí)現(xiàn)還會(huì)考慮優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題。
原理
既然mutex是一種二值信號(hào)量,因此就不需要像semaphore那樣需要一個(gè)count計(jì)數(shù)。由于mutex具有“持鎖者才能解鎖”的特點(diǎn),所以我們需要一個(gè)變量owner記錄持鎖進(jìn)程。釋放鎖的時(shí)候必須是同一個(gè)進(jìn)程才能釋放。當(dāng)然也需要一個(gè)鏈表頭,主要用來(lái)便利睡眠等待的進(jìn)程。原理和semaphore及其相似,因此在代碼上也有體現(xiàn)。
實(shí)現(xiàn)
mutex的實(shí)現(xiàn)代碼和linux中實(shí)現(xiàn)會(huì)有差異,但是依然可以為你呈現(xiàn)設(shè)計(jì)的原理。下面的設(shè)計(jì)代碼更像是部分RTOS中的代碼。mutex和semaphore一樣,我們需要兩個(gè)類似的結(jié)構(gòu)體分別描述mutex。
struct mutex_waiter {
struct list_head list;
struct task_struct *task;
};
struct mutex {
long owner;
struct list_head wait_list;
};
struct mutex_waiter的list成員是當(dāng)進(jìn)程無(wú)法獲取互斥量的時(shí)候掛入mutex的wait_list鏈表。
首先實(shí)現(xiàn)申請(qǐng)互斥量的函數(shù)。
void mutex_take(struct mutex *mutex)
{
struct mutex_waiter waiter;
if (!mutex->owner) {
mutex->owner = (long)current; /* 1 */
return;
}
waiter.task = current;
list_add_tail(&waiter.list, &mutex->wait_list); /* 2 */
schedule(); /* 2 */
}
當(dāng)mutex->owner的值為0的時(shí)候,代表沒(méi)有任何進(jìn)程持有鎖。因此可以直接申請(qǐng)成功。然后,記錄當(dāng)前申請(qǐng)鎖進(jìn)程的task_struct。
既然不能獲取互斥量,自然就需要睡眠等待,掛入等待鏈表。
互斥量的釋放代碼實(shí)現(xiàn)也同樣和semaphore有很多相似之處。不信,你看。
int mutex_release(struct mutex *mutex)
{
struct mutex_waiter *waiter;
if (mutex->owner != (long)current) /* 1 */
return -1;
if (list_empty(&mutex->wait_list)) {
mutex->owner = 0; /* 2 */
return 0;
}
waiter = list_first_entry(&mutex->wait_list, struct mutex_waiter, list);
list_del(&waiter->list);
mutex->owner = (long)waiter->task; /* 3 */
wake_up_process(waiter->task); /* 4 */
return 0;
}
mutex具有“持鎖者才能解鎖”的特點(diǎn)就是在這行代碼體現(xiàn)。
如果等待鏈表沒(méi)有進(jìn)程,那么自然只需要將mutex->owner置0,代表沒(méi)有鎖是釋放狀態(tài)。
mutex->owner的值改成當(dāng)前可以持鎖進(jìn)程的task_struct。
從等待進(jìn)程鏈表取出第一個(gè)進(jìn)程,并從鏈表上移除。然后就是喚醒該進(jìn)程。
評(píng)論
查看更多