新聞中心
Linux操作系統(tǒng)是一款自由、開(kāi)放、高效的操作系統(tǒng)。它的底層機(jī)制和算法非常具有代表性,工作隊(duì)列就是其中一個(gè)非常重要的實(shí)現(xiàn)機(jī)制。在本文中,我們將深入解析Linux工作隊(duì)列的實(shí)現(xiàn)機(jī)制,包括隊(duì)列的實(shí)現(xiàn)、類(lèi)型的區(qū)別、實(shí)現(xiàn)原理以及如何使用。

成都創(chuàng)新互聯(lián)公司始終堅(jiān)持【策劃先行,效果至上】的經(jīng)營(yíng)理念,通過(guò)多達(dá)十余年累計(jì)超上千家客戶(hù)的網(wǎng)站建設(shè)總結(jié)了一套系統(tǒng)有效的推廣解決方案,現(xiàn)已廣泛運(yùn)用于各行各業(yè)的客戶(hù),其中包括:紗窗等企業(yè),備受客戶(hù)稱(chēng)贊。
一、工作隊(duì)列概述
工作隊(duì)列是Linux中一種很重要的異步操作機(jī)制,它允許驅(qū)動(dòng)程序、內(nèi)核模塊執(zhí)行一些長(zhǎng)時(shí)間處理的操作,而不會(huì)使調(diào)用者產(chǎn)生阻塞??梢岳斫鉃槭且环N異步、非阻塞的操作方式。
工作隊(duì)列基本上是由一個(gè)等待隊(duì)列(等待隊(duì)列是一個(gè)進(jìn)程阻塞隊(duì)列)和一個(gè)內(nèi)核線(xiàn)程組成的,當(dāng)隊(duì)列中加入一個(gè)工作項(xiàng)時(shí),它就會(huì)被添加到等待隊(duì)列中,然后線(xiàn)程就會(huì)醒來(lái),從等待隊(duì)列中取出工作項(xiàng)并進(jìn)行處理。
二、工作隊(duì)列的實(shí)現(xiàn)
工作隊(duì)列在內(nèi)核中是非常常見(jiàn)和重要的實(shí)現(xiàn)機(jī)制,下圖為L(zhǎng)inux內(nèi)核中實(shí)現(xiàn)工作隊(duì)列的架構(gòu)圖。
圖1 工作隊(duì)列的架構(gòu)圖
如圖所示,工作隊(duì)列分為兩個(gè)部分:內(nèi)核線(xiàn)程和工作項(xiàng)。
內(nèi)核線(xiàn)程
內(nèi)核線(xiàn)程是Linux內(nèi)核中非常特殊的一種進(jìn)程,它完全受內(nèi)核控制,被用來(lái)執(zhí)行內(nèi)核代碼、完成內(nèi)核任務(wù)。這種進(jìn)程并不受任何用戶(hù)進(jìn)程的控制,而是由內(nèi)核自行創(chuàng)建和維護(hù)。當(dāng)把一個(gè)工作項(xiàng)放到工作隊(duì)列中后,內(nèi)核線(xiàn)程就會(huì)去執(zhí)行這個(gè)工作項(xiàng),執(zhí)行完成后,內(nèi)核將它退出,并回收相應(yīng)的資源。
內(nèi)核線(xiàn)程更具有代表性的兩個(gè)函數(shù)為kernel_thread()和kthread_create()。
*kernel_thread()*
在Linux內(nèi)核中創(chuàng)建線(xiàn)程的方式非常有特色,它不同于其他操作系統(tǒng)把線(xiàn)程和進(jìn)程作為兩個(gè)獨(dú)立的概念,而是把線(xiàn)程看作是一種特殊的進(jìn)程,用另一種方式來(lái)創(chuàng)建它。
*kthread_create()*
kthread_create()是在Linux內(nèi)核中專(zhuān)門(mén)用于創(chuàng)建內(nèi)核線(xiàn)程的函數(shù),它的實(shí)現(xiàn)方式是在kernel_thread()基礎(chǔ)上加上了一些特殊的參數(shù)來(lái)使創(chuàng)建的進(jìn)程成為內(nèi)核線(xiàn)程。
工作項(xiàng)
工作項(xiàng)是實(shí)際執(zhí)行的操作,當(dāng)一個(gè)工作項(xiàng)被加入到工作隊(duì)列時(shí),它實(shí)際上就被加入到了一個(gè)等待隊(duì)列中,等待內(nèi)核線(xiàn)程來(lái)執(zhí)行。在內(nèi)核中,工作項(xiàng)通常使用struct work_struct結(jié)構(gòu)體來(lái)表示。
工作項(xiàng)的初始化方法為INIT_WORK(work, func),其中work為結(jié)構(gòu)體指針,func為回調(diào)函數(shù),用來(lái)指定這個(gè)工作項(xiàng)需要執(zhí)行的任務(wù)。
工作項(xiàng)狀態(tài)轉(zhuǎn)換如下:
WORK_STATE_PENDING->WORK_STATE_SCHEDULED->WORK_STATE_RUNNING->WORK_STATE_DONE
?。ǖ却?>計(jì)劃中->執(zhí)行中->完成)
工作項(xiàng)狀態(tài)的變化都是由內(nèi)核線(xiàn)程進(jìn)行的,所以在實(shí)現(xiàn)時(shí)需要仔細(xì)考慮它們之間的交互關(guān)系。
三、工作隊(duì)列類(lèi)型的區(qū)別
Linux內(nèi)核中有兩種工作隊(duì)列類(lèi)型:立即工作隊(duì)列和延遲工作隊(duì)列。
立即工作隊(duì)列
當(dāng)一個(gè)新的工作項(xiàng)被加入到隊(duì)列中時(shí),對(duì)應(yīng)的內(nèi)核線(xiàn)程(kworker)將立即被喚醒來(lái)執(zhí)行這個(gè)工作項(xiàng)。此類(lèi)型的工作隊(duì)列主要是為了能夠及時(shí)地處理一些任務(wù),如實(shí)時(shí)視頻播放、音頻處理等,以保證系統(tǒng)的流暢性和即時(shí)性。
延遲工作隊(duì)列
延遲工作隊(duì)列和立即工作隊(duì)列的區(qū)別在于,當(dāng)一個(gè)新的工作項(xiàng)被加入到隊(duì)列中時(shí),等待一段時(shí)間后再由對(duì)應(yīng)的內(nèi)核線(xiàn)程執(zhí)行。這種工作隊(duì)列用于標(biāo)準(zhǔn)進(jìn)程的一些需要優(yōu)化的場(chǎng)景中,如系統(tǒng)開(kāi)機(jī)和關(guān)機(jī)的部分處理操作。
四、工作隊(duì)列的實(shí)現(xiàn)原理
工作隊(duì)列的實(shí)現(xiàn)是基于一個(gè)稱(chēng)為Kthread的內(nèi)核線(xiàn)程模塊。一個(gè)Kthread是一個(gè)內(nèi)核線(xiàn)程,它可以喚醒、安排和執(zhí)行工作隊(duì)列上的工作項(xiàng)。Kthread模塊包括了一個(gè)線(xiàn)程管理結(jié)構(gòu)體、一個(gè)定時(shí)器和工作隊(duì)列本身。
線(xiàn)程管理結(jié)構(gòu)體:kthread_t結(jié)構(gòu)體用于內(nèi)核線(xiàn)程的創(chuàng)建、發(fā)起、掛掉和結(jié)束等操作。
定時(shí)器:timer_list結(jié)構(gòu)體定義了一個(gè)定時(shí)器,要保證在延遲隊(duì)列的實(shí)現(xiàn)中表現(xiàn)得像工作項(xiàng)一樣,并杜絕了在等待隊(duì)列中等待太長(zhǎng)時(shí)間的情況。
工作隊(duì)列本身:DECLARE_WORK宏定義了一個(gè)工作項(xiàng),用于存儲(chǔ)待執(zhí)行的工作標(biāo)識(shí)、對(duì)應(yīng)的處理函數(shù)等等。每個(gè)工作項(xiàng)都和一個(gè)timer_list和kthread_t結(jié)構(gòu)體綁定在一起。
五、工作隊(duì)列的使用方法
使用工作隊(duì)列是為了把一些處理操作放到一個(gè)單獨(dú)的線(xiàn)程中執(zhí)行,而不是在使用線(xiàn)程池的情況下,等待一個(gè)可用的線(xiàn)程去執(zhí)行。這個(gè)方法既簡(jiǎn)便易行,也是保證Linux設(shè)備驅(qū)動(dòng)程序高性能運(yùn)行的有效手段之一。
我們可以通過(guò)聲明并定義一個(gè)工作項(xiàng)來(lái)實(shí)現(xiàn),工作項(xiàng)創(chuàng)建完成后,我們就可以使用schedule_work()函數(shù)將它添加到需要的工作隊(duì)列中等待執(zhí)行。在工作項(xiàng)的回調(diào)函數(shù)中寫(xiě)入具體的操作指令。
六、工作隊(duì)列的注意事項(xiàng)
1. 工作隊(duì)列給系統(tǒng)開(kāi)銷(xiāo)帶來(lái)一定的影響,尤其是對(duì)立即工作隊(duì)列,并不適合在實(shí)時(shí)系統(tǒng)中大量運(yùn)用。
2. 在使用工作隊(duì)列時(shí),需要格外注意內(nèi)核的限制和規(guī)定。由于工作項(xiàng)被安排到內(nèi)核線(xiàn)程上,大部分操作都是在內(nèi)核命名空間中進(jìn)行的,因此需要注意內(nèi)核線(xiàn)程中的常見(jiàn)限制,如不能訪(fǎng)問(wèn)用戶(hù)空間的任何數(shù)據(jù)等等。
3. 工作隊(duì)列是Linux內(nèi)核的一個(gè)非常復(fù)雜的實(shí)現(xiàn)機(jī)制,了解它的原理和使用方式非常重要。同時(shí),在使用過(guò)程中需要仔細(xì)考慮與其他系統(tǒng)資源的協(xié)同,才能更好地實(shí)現(xiàn)Linux系統(tǒng)的高效性和穩(wěn)定性。
結(jié)語(yǔ)
Linux工作隊(duì)列是我們?cè)谶M(jìn)行Linux內(nèi)核開(kāi)發(fā)時(shí)必須要了解的一個(gè)重要機(jī)制,掌握工作隊(duì)列的原理和使用必將會(huì)使我們成為一名更為出色的開(kāi)發(fā)工程師。希望本文對(duì)大家能夠有所幫助。
相關(guān)問(wèn)題拓展閱讀:
- 《Linux設(shè)備驅(qū)動(dòng)程序》(十六)-中斷處理
- 如何實(shí)現(xiàn)linux下多線(xiàn)程之間的互斥與同步
《Linux設(shè)備驅(qū)動(dòng)程序》(十六)-中斷處理
設(shè)備與處理器之間的工作通常來(lái)說(shuō)是異步,設(shè)備數(shù)據(jù)要傳遞給處理器通常來(lái)說(shuō)有以下幾種方法:輪詢(xún)、等待和中斷。
讓CPU進(jìn)行輪詢(xún)等待總是不能讓人滿(mǎn)意,所以通常都采用中斷的形式,讓設(shè)備來(lái)通知CPU讀取數(shù)據(jù)。
2.6內(nèi)核的函數(shù)參數(shù)與現(xiàn)在的參數(shù)有所區(qū)別,這里都主要介紹概念,具體實(shí)現(xiàn)方法需要結(jié)合具體的內(nèi)核版本。
request_irq函數(shù)申請(qǐng)中斷,返回0表示申請(qǐng)成功,其他返回值表示申請(qǐng)失敗,其具體參數(shù)解釋如下:
flags 掩碼可以使用以下幾個(gè):
快速和慢速處理例程
:現(xiàn)代內(nèi)核中基本沒(méi)有這兩個(gè)概念了,使用SA_INTERRUPT位后,當(dāng)中斷被執(zhí)行時(shí),當(dāng)前處理器的其他中斷都將被禁止。通常不要使用SA_INTERRUPT標(biāo)志位,除非自己明確知道會(huì)發(fā)生什么。
共享中斷
:使用共享中斷時(shí),一方面要使用SA_SHIRQ位,另一個(gè)是request_irq中的dev_id必須是唯一的,不能為NULL。這個(gè)限制的原因是:內(nèi)核為每個(gè)中斷維護(hù)了一個(gè)共享處理例程的列表,例程中的dev_id各不相同,就像設(shè)備簽名。如果dev_id相同,在卸載的跡鎮(zhèn)橋時(shí)候引起混淆(卸載了另一個(gè)中斷),當(dāng)中斷到達(dá)時(shí)會(huì)產(chǎn)生內(nèi)核OOP消息。
共享中斷需要滿(mǎn)足以下一個(gè)條件才能申請(qǐng)成功:
當(dāng)不需要使用該中斷時(shí),需要使用free_irq釋放中斷。
通常我們會(huì)在模塊加載的時(shí)候申請(qǐng)安裝中斷處理例程,但書(shū)中建議:在設(shè)備之一次打開(kāi)的時(shí)候安裝,在設(shè)備最后一次關(guān)閉的時(shí)候卸載。
如果要查看中斷觸發(fā)的次數(shù),可以查看 /proc/interrupts 和 /proc/stat。
書(shū)中講述了如何自動(dòng)檢測(cè)中斷號(hào),在嵌入式開(kāi)發(fā)中通常都是查看原理圖和datasheet來(lái)直接確定。
自動(dòng)檢測(cè)的原理如下:驅(qū)動(dòng)程序通知設(shè)備產(chǎn)生中斷,然后查看哪些中斷信號(hào)線(xiàn)被觸發(fā)了。Linux提供了以下方法來(lái)進(jìn)行探測(cè):
探測(cè)工作耗時(shí)較長(zhǎng),建議在模塊加載的時(shí)候做。
中斷處理函數(shù)和普通函數(shù)其實(shí)差不多,唯一的區(qū)別是其運(yùn)行的中斷上下文中,在這個(gè)上下文中有以下注意事項(xiàng):
中斷處理函數(shù)典型用法如下:
中斷處理函數(shù)的參數(shù)和返回值含義如下:
返回值主要有兩個(gè):IRQ_NONE和IRQ_HANDLED。
對(duì)于中斷我們是可以進(jìn)行開(kāi)啟和關(guān)閉的,Linux中提供了以下函數(shù)操作單個(gè)中斷姿猛的開(kāi)關(guān):
該方法可以在所有處理器上禁止或啟用中斷。
需要注意的是:
如果要關(guān)閉當(dāng)前處理器上所有的中斷,則可以調(diào)用以下方法:
local_irq_save 會(huì)將中斷狀態(tài)保持到flags中,然后禁用處理器上的中斷;如果明確知道中斷沒(méi)有在其他地方被禁用,則可以使用local_irq_disable,否則請(qǐng)使用local_irq_save。
locat_irq_restore 會(huì)根據(jù)上面獲取到flags來(lái)恢復(fù)中斷;local_irq_enable 會(huì)無(wú)條件打開(kāi)所有中斷。
在中斷中需要做一些工作,如果工作內(nèi)容太多,必然導(dǎo)致中斷處理所需的時(shí)間過(guò)長(zhǎng);而中斷處理又要求能夠盡快完成,這樣才不會(huì)影響正常的系統(tǒng)調(diào)度,這兩個(gè)之間就產(chǎn)生了矛盾旅螞。
現(xiàn)在很多操作系統(tǒng)將中斷分為兩個(gè)部分來(lái)處理上面的矛盾:頂半部和底半部。
頂半部就是我們用request_irq來(lái)注冊(cè)的中斷處理函數(shù),這個(gè)函數(shù)要求能夠盡快結(jié)束,同時(shí)在其中調(diào)度底半部,讓底半部在之后來(lái)進(jìn)行后續(xù)的耗時(shí)工作。
頂半部就不再說(shuō)明了,就是上面的中斷處理函數(shù),只是要求能夠盡快處理完成并返回,不要處理耗時(shí)工作。
底半部通常使用tasklet或者工作隊(duì)列來(lái)實(shí)現(xiàn)。
tasklet的特點(diǎn)和注意事項(xiàng):
工作隊(duì)列的特點(diǎn)和注意事項(xiàng):
如何實(shí)現(xiàn)linux下多線(xiàn)程之間的互斥與同步
Linux設(shè)備驅(qū)動(dòng)中必須解決的一個(gè)問(wèn)題是多個(gè)進(jìn)程對(duì)共享資源的并發(fā)訪(fǎng)問(wèn),并發(fā)訪(fǎng)問(wèn)會(huì)導(dǎo)致競(jìng)態(tài),linux提供了多種解決競(jìng)態(tài)問(wèn)題的方式,這些方式適合不同的應(yīng)用場(chǎng)景。
Linux內(nèi)核是多進(jìn)程、多線(xiàn)程的操作系統(tǒng),它提供了相當(dāng)完整的內(nèi)核同步方法。內(nèi)核同步方法列表如下:
中斷屏蔽
原子操作
自旋鎖
讀寫(xiě)自旋鎖
順序鎖
信號(hào)量
讀寫(xiě)信號(hào)量
BKL(大內(nèi)核鎖)
Seq鎖
一、并發(fā)與競(jìng)態(tài):
定義:
并發(fā)(concurrency)指的是多個(gè)執(zhí)行單元同時(shí)、并行被執(zhí)行,而并發(fā)的執(zhí)行單元對(duì)共享資源(硬件資源和軟件上的全局變量、靜態(tài)變量等)的訪(fǎng)問(wèn)則很容易導(dǎo)致競(jìng)態(tài)(race conditions)。
在linux中,主要的競(jìng)態(tài)發(fā)生在如下幾種情況:
1、對(duì)稱(chēng)多處理器歷辯埋(P)多個(gè)CPU
特點(diǎn)是多個(gè)CPU使用共同的系統(tǒng)總線(xiàn),因此可訪(fǎng)問(wèn)共同的外設(shè)和存儲(chǔ)器。
2、單CPU內(nèi)進(jìn)程與搶占它的進(jìn)程
3、中斷(硬中斷、軟中斷、Tasklet、底半部)與進(jìn)程之間
只要并發(fā)的多個(gè)執(zhí)行單元存在對(duì)共享資源的訪(fǎng)問(wèn),競(jìng)態(tài)就有可能發(fā)生。
如果中斷處理程序訪(fǎng)問(wèn)進(jìn)程正在訪(fǎng)問(wèn)的資源,則競(jìng)態(tài)也會(huì)會(huì)發(fā)生。
多個(gè)中斷之間本身也可能引起并發(fā)而導(dǎo)致競(jìng)態(tài)(中斷被更高優(yōu)先級(jí)的中斷打斷)。
解決競(jìng)態(tài)問(wèn)題的途徑是保證對(duì)共享資源的互斥訪(fǎng)問(wèn),所謂互斥訪(fǎng)問(wèn)就是指一個(gè)執(zhí)行單元在訪(fǎng)問(wèn)共享資源的時(shí)候,其他的執(zhí)行單元都被禁止訪(fǎng)問(wèn)。
訪(fǎng)問(wèn)共享資源的代碼區(qū)域被稱(chēng)為臨界區(qū),臨界區(qū)需要以某種互斥機(jī)制加以保護(hù),中斷屏蔽,原子操作,自旋鎖,和信號(hào)量都是linux設(shè)備驅(qū)動(dòng)中可采用的互斥途徑。
臨界區(qū)和競(jìng)爭(zhēng)條件:
所謂臨界區(qū)(critical regions)就是訪(fǎng)問(wèn)和操作共享數(shù)據(jù)的代碼段,為了避免在臨界區(qū)中并發(fā)訪(fǎng)問(wèn),編程者必須保證這些代碼原子地執(zhí)行——也就是說(shuō),代碼在執(zhí)行結(jié)束前不可被打斷,就如同整個(gè)臨界區(qū)是一個(gè)不可分割的指令一樣,如果兩個(gè)執(zhí)行線(xiàn)程有可能處于同一個(gè)臨界區(qū)中,那么就是程序包含一個(gè)bug,如果這種情況發(fā)生了,我們就稱(chēng)之為競(jìng)爭(zhēng)條件(race conditions),避免并發(fā)和防止競(jìng)爭(zhēng)條件被稱(chēng)為同步。
死鎖:
死鎖的產(chǎn)生需要一定條件:要有一個(gè)或多個(gè)執(zhí)行線(xiàn)程和一個(gè)或多個(gè)資源,每個(gè)線(xiàn)程都在等待其中的一個(gè)資源,但所有的資源都已經(jīng)被占用了,所有線(xiàn)程都在相互等待,但它們永遠(yuǎn)不會(huì)釋放已經(jīng)占有的資源,于是任何線(xiàn)程都無(wú)法繼續(xù),這便意味著死鎖的發(fā)生。
二、中斷屏蔽
在單CPU范圍內(nèi)避免競(jìng)態(tài)的一種簡(jiǎn)單方法是在進(jìn)入臨界區(qū)之前屏蔽系統(tǒng)的中斷。
由于linux內(nèi)核的進(jìn)程調(diào)度等操作都依賴(lài)中斷來(lái)實(shí)現(xiàn),內(nèi)核搶占進(jìn)程之間的并發(fā)也就得以避免了。
中斷屏蔽的使用方法:
local_irq_disable()//屏蔽中斷
//臨界區(qū)
local_irq_enable()//灶念開(kāi)中斷
特點(diǎn):
由于linux系統(tǒng)的異步IO,進(jìn)程調(diào)度等很多重要操作都依賴(lài)于中斷,在屏蔽中斷期間所有的中斷都無(wú)法得到處理,因此長(zhǎng)時(shí)間的屏蔽是很危險(xiǎn)的,有可能造成數(shù)據(jù)丟失甚至系統(tǒng)崩潰,這就要求在屏蔽中斷之后,當(dāng)前的內(nèi)核執(zhí)行路徑應(yīng)當(dāng)盡快地執(zhí)行完臨界區(qū)的代碼。
中斷屏蔽只能禁止本CPU內(nèi)的中斷,因此,并不能解決多CPU引發(fā)的競(jìng)態(tài),所以單獨(dú)使用中斷屏蔽并不是一個(gè)值得推薦的避免競(jìng)態(tài)的方法,它一般和自旋鎖配合使用。
三、原子操作
定義:原子操作指的是在執(zhí)行過(guò)程中不會(huì)被別的代碼路徑所中斷的操作。
(原子原本指的是不可分割的微粒,所以原子操作也就是不能夠被分割的指令)
(它保證指令以“原子”的方式執(zhí)行而不能被打斷)
原子操作是不可分割的,在執(zhí)行完畢不會(huì)被任何其它任務(wù)或事件中斷。在單處理器系統(tǒng)(UniProcessor)中,能夠在單條指令中完成的操作都可以認(rèn)為是” 原子操作”,因?yàn)橹袛嘀荒馨l(fā)生于指令之間。這也是某些CPU指令系統(tǒng)中引入了test_and_set、test_and_clear等指令用于臨界資源互斥的原因。但是,在對(duì)稱(chēng)多處理器(Symmetric Multi-Processor)結(jié)構(gòu)中就不同了,由于系統(tǒng)中有多個(gè)處理器在獨(dú)立地運(yùn)行,即使能在單條指令中完成的操作也有可能受到干擾。我們以decl (遞減指令)為例,這是一個(gè)典型的”讀-改-肢螞寫(xiě)”過(guò)程,涉及兩次內(nèi)存訪(fǎng)問(wèn)。
通俗理解:
原子操作,顧名思義,就是說(shuō)像原子一樣不可再細(xì)分。一個(gè)操作是原子操作,意思就是說(shuō)這個(gè)操作是以原子的方式被執(zhí)行,要一口氣執(zhí)行完,執(zhí)行過(guò)程不能夠被OS的其他行為打斷,是一個(gè)整體的過(guò)程,在其執(zhí)行過(guò)程中,OS的其它行為是插不進(jìn)來(lái)的。
分類(lèi):linux內(nèi)核提供了一系列函數(shù)來(lái)實(shí)現(xiàn)內(nèi)核中的原子操作,分為整型原子操作和位原子操作,共同點(diǎn)是:在任何情況下操作都是原子的,內(nèi)核代碼可以安全的調(diào)用它們而不被打斷。
原子整數(shù)操作:
針對(duì)整數(shù)的原子操作只能對(duì)atomic_t類(lèi)型的數(shù)據(jù)進(jìn)行處理,在這里之所以引入了一個(gè)特殊的數(shù)據(jù)類(lèi)型,而沒(méi)有直接使用C語(yǔ)言的int型,主要是出于兩個(gè)原因:
之一、讓原子函數(shù)只接受atomic_t類(lèi)型的操作數(shù),可以確保原子操作只與這種特殊類(lèi)型數(shù)據(jù)一起使用,同時(shí),這也確保了該類(lèi)型的數(shù)據(jù)不會(huì)被傳遞給其它任何非原子函數(shù);
第二、使用atomic_t類(lèi)型確保編譯器不對(duì)相應(yīng)的值進(jìn)行訪(fǎng)問(wèn)優(yōu)化——這點(diǎn)使得原子操作最終接收到正確的內(nèi)存地址,而不是一個(gè)別名,最后就是在不同體系結(jié)構(gòu)上實(shí)現(xiàn)原子操作的時(shí)候,使用atomic_t可以屏蔽其間的差異。
原子整數(shù)操作最常見(jiàn)的用途就是實(shí)現(xiàn)計(jì)數(shù)器。
另一點(diǎn)需要說(shuō)明原子操作只能保證操作是原子的,要么完成,要么不完成,不會(huì)有操作一半的可能,但原子操作并不能保證操作的順序性,即它不能保證兩個(gè)操作是按某個(gè)順序完成的。如果要保證原子操作的順序性,請(qǐng)使用內(nèi)存屏障指令。
atomic_t和ATOMIC_INIT(i)定義
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
在你編寫(xiě)代碼的時(shí)候,能使用原子操作的時(shí)候,就盡量不要使用復(fù)雜的加鎖機(jī)制,對(duì)多數(shù)體系結(jié)構(gòu)來(lái)講,原子操作與更復(fù)雜的同步方法相比較,給系統(tǒng)帶來(lái)的開(kāi)銷(xiāo)小,對(duì)高速緩存行的影響也小,但是,對(duì)于那些有高性能要求的代碼,對(duì)多種同步方法進(jìn)行測(cè)試比較,不失為一種明智的作法。
原子位操作:
針對(duì)位這一級(jí)數(shù)據(jù)進(jìn)行操作的函數(shù),是對(duì)普通的內(nèi)存地址進(jìn)行操作的。它的參數(shù)是一個(gè)指針和一個(gè)位號(hào)。
為方便其間,內(nèi)核還提供了一組與上述操作對(duì)應(yīng)的非原子位函數(shù),非原子位函數(shù)與原子位函數(shù)的操作完全相同,但是,前者不保證原子性,且其名字前綴多兩個(gè)下劃線(xiàn)。例如,與test_bit()對(duì)應(yīng)的非原子形式是_test_bit(),如果你不需要原子性操作(比如,如果你已經(jīng)用鎖保護(hù)了自己的數(shù)據(jù)),那么這些非原子的位函數(shù)相比原子的位函數(shù)可能會(huì)執(zhí)行得更快些。
四、自旋鎖
自旋鎖的引入:
如 果每個(gè)臨界區(qū)都能像增加變量這樣簡(jiǎn)單就好了,可惜現(xiàn)實(shí)不是這樣,而是臨界區(qū)可以跨越多個(gè)函數(shù),例如:先得從一個(gè)數(shù)據(jù)結(jié)果中移出數(shù)據(jù),對(duì)其進(jìn)行格式轉(zhuǎn)換和解 析,最后再把它加入到另一個(gè)數(shù)據(jù)結(jié)構(gòu)中,整個(gè)執(zhí)行過(guò)程必須是原子的,在數(shù)據(jù)被更新完畢之前,不能有其他代碼讀取這些數(shù)據(jù),顯然,簡(jiǎn)單的原子操作是無(wú)能為力 的(在單處理器系統(tǒng)(UniProcessor)中,能夠在單條指令中完成的操作都可以認(rèn)為是” 原子操作”,因?yàn)橹袛嘀荒馨l(fā)生于指令之間),這就需要使用更為復(fù)雜的同步方法——鎖來(lái)提供保護(hù)。
自旋鎖的介紹:
Linux內(nèi)核中最常見(jiàn)的鎖是自旋鎖(spin lock),自旋鎖最多只能被一個(gè)可執(zhí)行線(xiàn)程持有,如果一個(gè)執(zhí)行線(xiàn)程試圖獲得一個(gè)被爭(zhēng)用(已經(jīng)被持有)的自旋鎖,那么該線(xiàn)程就會(huì)一直進(jìn)行忙循環(huán)—旋轉(zhuǎn)—等待鎖重新可用,要是鎖未被爭(zhēng)用,請(qǐng)求鎖的執(zhí)行線(xiàn)程便能立刻得到它,繼續(xù)執(zhí)行,在任意時(shí)間,自旋鎖都可以防止多于一個(gè)的執(zhí)行線(xiàn)程同時(shí)進(jìn)入理解區(qū),注意同一個(gè)鎖可以用在多個(gè)位置—例如,對(duì)于給定數(shù)據(jù)的所有訪(fǎng)問(wèn)都可以得到保護(hù)和同步。
一個(gè)被爭(zhēng)用的自旋鎖使得請(qǐng)求它的線(xiàn)程在等待鎖重新可用時(shí)自旋(特別浪費(fèi)處理器時(shí)間),所以自旋鎖不應(yīng)該被長(zhǎng)時(shí)間持有,事實(shí)上,這點(diǎn)正是使用自旋鎖的初衷,在短期間內(nèi)進(jìn)行輕量級(jí)加鎖,還可以采取另外的方式來(lái)處理對(duì)鎖的爭(zhēng)用:讓請(qǐng)求線(xiàn)程睡眠,直到鎖重新可用時(shí)再喚醒它,這樣處理器就不必循環(huán)等待,可以去執(zhí)行其他代碼,這也會(huì)帶來(lái)一定的開(kāi)銷(xiāo)——這里有兩次明顯的上下文切換, 被阻塞的線(xiàn)程要換出和換入。因此,持有自旋鎖的時(shí)間更好小于完成兩次上下文切換的耗時(shí),當(dāng)然我們大多數(shù)人不會(huì)無(wú)聊到去測(cè)量上下文切換的耗時(shí),所以我們讓持 有自旋鎖的時(shí)間應(yīng)盡可能的短就可以了,信號(hào)量可以提供上述第二種機(jī)制,它使得在發(fā)生爭(zhēng)用時(shí),等待的線(xiàn)程能投入睡眠,而不是旋轉(zhuǎn)。
自旋鎖可以使用在中斷處理程序中(此處不能使用信號(hào)量,因?yàn)樗鼈儠?huì)導(dǎo)致睡眠),在中斷處理程序中使用自旋鎖時(shí),一定要在獲取鎖之前,首先禁止本地中斷(在 當(dāng)前處理器上的中斷請(qǐng)求),否則,中斷處理程序就會(huì)打斷正持有鎖的內(nèi)核代碼,有可能會(huì)試圖去爭(zhēng)用這個(gè)已經(jīng)持有的自旋鎖,這樣以來(lái),中斷處理程序就會(huì)自旋, 等待該鎖重新可用,但是鎖的持有者在這個(gè)中斷處理程序執(zhí)行完畢前不可能運(yùn)行,這正是我們?cè)谇耙徽鹿?jié)中提到的雙重請(qǐng)求死鎖,注意,需要關(guān)閉的只是當(dāng)前處理器上的中斷,如果中斷發(fā)生在不同的處理器上,即使中斷處理程序在同一鎖上自旋,也不會(huì)妨礙鎖的持有者(在不同處理器上)最終釋放鎖。
自旋鎖的簡(jiǎn)單理解:
理解自旋鎖最簡(jiǎn)單的方法是把它作為一個(gè)變量看待,該變量把一個(gè)臨界區(qū)或者標(biāo)記為“我當(dāng)前正在運(yùn)行,請(qǐng)稍等一會(huì)”或者標(biāo)記為“我當(dāng)前不在運(yùn)行,可以被使用”。如果A執(zhí)行單元首先進(jìn)入例程,它將持有自旋鎖,當(dāng)B執(zhí)行單元試圖進(jìn)入同一個(gè)例程時(shí),將獲知自旋鎖已被持有,需等到A執(zhí)行單元釋放后才能進(jìn)入。
自旋鎖的API函數(shù):
其實(shí)介紹的幾種信號(hào)量和互斥機(jī)制,其底層源碼都是使用自旋鎖,可以理解為自旋鎖的再包裝。所以從這里就可以理解為什么自旋鎖通??梢蕴峁┍刃盘?hào)量更高的性能。
自旋鎖是一個(gè)互斥設(shè)備,他只能會(huì)兩個(gè)值:“鎖定”和“解鎖”。它通常實(shí)現(xiàn)為某個(gè)整數(shù)之中的單個(gè)位。
“測(cè)試并設(shè)置”的操作必須以原子方式完成。
任何時(shí)候,只要內(nèi)核代碼擁有自旋鎖,在相關(guān)CPU上的搶占就會(huì)被禁止。
適用于自旋鎖的核心規(guī)則:
(1)任何擁有自旋鎖的代碼都必須使原子的,除服務(wù)中斷外(某些情況下也不能放棄CPU,如中斷服務(wù)也要獲得自旋鎖。為了避免這種鎖陷阱,需要在擁有自旋鎖時(shí)禁止中斷),不能放棄CPU(如休眠,休眠可發(fā)生在許多無(wú)法預(yù)期的地方)。否則CPU將有可能永遠(yuǎn)自旋下去(死機(jī))。
(2)擁有自旋鎖的時(shí)間越短越好。
需 要強(qiáng)調(diào)的是,自旋鎖別設(shè)計(jì)用于多處理器的同步機(jī)制,對(duì)于單處理器(對(duì)于單處理器并且不可搶占的內(nèi)核來(lái)說(shuō),自旋鎖什么也不作),內(nèi)核在編譯時(shí)不會(huì)引入自旋鎖 機(jī)制,對(duì)于可搶占的內(nèi)核,它僅僅被用于設(shè)置內(nèi)核的搶占機(jī)制是否開(kāi)啟的一個(gè)開(kāi)關(guān),也就是說(shuō)加鎖和解鎖實(shí)際變成了禁止或開(kāi)啟內(nèi)核搶占功能。如果內(nèi)核不支持搶 占,那么自旋鎖根本就不會(huì)編譯到內(nèi)核中。
內(nèi)核中使用spinlock_t類(lèi)型來(lái)表示自旋鎖,它定義在:
typedef struct {
raw_spinlock_t raw_lock;
#if defined(CONFIG_PREEMPT) && defined(CONFIG_P)
unsigned int break_lock;
#endif
} spinlock_t;
對(duì)于不支持P的內(nèi)核來(lái)說(shuō),struct raw_spinlock_t什么也沒(méi)有,是一個(gè)空結(jié)構(gòu)。對(duì)于支持多處理器的內(nèi)核來(lái)說(shuō),struct raw_spinlock_t定義為
typedef struct {
unsigned int slock;
} raw_spinlock_t;
slock表示了自旋鎖的狀態(tài),“1”表示自旋鎖處于解鎖狀態(tài)(UNLOCK),“0”表示自旋鎖處于上鎖狀態(tài)(LOCKED)。
break_lock表示當(dāng)前是否由進(jìn)程在等待自旋鎖,顯然,它只有在支持搶占的P內(nèi)核上才起作用。
自旋鎖的實(shí)現(xiàn)是一個(gè)復(fù)雜的過(guò)程,說(shuō)它復(fù)雜不是因?yàn)樾枰嗌俅a或邏輯來(lái)實(shí)現(xiàn)它,其實(shí)它的實(shí)現(xiàn)代碼很少。自旋鎖的實(shí)現(xiàn)跟體系結(jié)構(gòu)關(guān)系密切,核心代碼基本也是由匯編語(yǔ)言寫(xiě)成,與體協(xié)結(jié)構(gòu)相關(guān)的核心代碼都放在相關(guān)的目錄下,比如。對(duì)于我們驅(qū)動(dòng)程序開(kāi)發(fā)人員來(lái)說(shuō),我們沒(méi)有必要了解這么spinlock的內(nèi)部細(xì)節(jié),如果你對(duì)它感興趣,請(qǐng)參考閱讀Linux內(nèi)核源代碼。對(duì)于我們驅(qū)動(dòng)的spinlock接口,我們只需包括頭文件。在我們?cè)敿?xì)的介紹spinlock的API之前,我們先來(lái)看看自旋鎖的一個(gè)基本使用格式:
#include
spinlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock(&lock);
….
spin_unlock(&lock);
從使用上來(lái)說(shuō),spinlock的API還很簡(jiǎn)單的,一般我們會(huì)用的的API如下表,其實(shí)它們都是定義在中的宏接口,真正的實(shí)現(xiàn)在中
#include
SPIN_LOCK_UNLOCKED
DEFINE_SPINLOCK
spin_lock_init( spinlock_t *)
spin_lock(spinlock_t *)
spin_unlock(spinlock_t *)
spin_lock_irq(spinlock_t *)
spin_unlock_irq(spinlock_t *)
spin_lock_irqsace(spinlock_t *,unsigned long flags)
spin_unlock_irqsace(spinlock_t *, unsigned long flags)
spin_trylock(spinlock_t *)
spin_is_locked(spinlock_t *)
? 初始化
spinlock有兩種初始化形式,一種是靜態(tài)初始化,一種是動(dòng)態(tài)初始化。對(duì)于靜態(tài)的spinlock對(duì)象,我們用 SPIN_LOCK_UNLOCKED來(lái)初始化,它是一個(gè)宏。當(dāng)然,我們也可以把聲明spinlock和初始化它放在一起做,這就是 DEFINE_SPINLOCK宏的工作,因此,下面的兩行代碼是等價(jià)的。
DEFINE_SPINLOCK (lock);
spinlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock_init 函數(shù)一般用來(lái)初始化動(dòng)態(tài)創(chuàng)建的spinlock_t對(duì)象,它的參數(shù)是一個(gè)指向spinlock_t對(duì)象的指針。當(dāng)然,它也可以初始化一個(gè)靜態(tài)的沒(méi)有初始化的spinlock_t對(duì)象。
spinlock_t *lock
……
spin_lock_init(lock);
? 獲取鎖
內(nèi)核提供了三個(gè)函數(shù)用于獲取一個(gè)自旋鎖。
spin_lock:獲取指定的自旋鎖。
spin_lock_irq:禁止本地中斷并獲取自旋鎖。
spin_lock_irqsace:保存本地中斷狀態(tài),禁止本地中斷并獲取自旋鎖,返回本地中斷狀態(tài)。
自旋鎖是可以使用在中斷處理程序中的,這時(shí)需要使用具有關(guān)閉本地中斷功能的函數(shù),我們推薦使用 spin_lock_irqsave,因?yàn)樗鼤?huì)保存加鎖前的中斷標(biāo)志,這樣就會(huì)正確恢復(fù)解鎖時(shí)的中斷標(biāo)志。如果spin_lock_irq在加鎖時(shí)中斷是關(guān)閉的,那么在解鎖時(shí)就會(huì)錯(cuò)誤的開(kāi)啟中斷。
另外兩個(gè)同自旋鎖獲取相關(guān)的函數(shù)是:
spin_trylock():嘗試獲取自旋鎖,如果獲取失敗則立即返回非0值,否則返回0。
spin_is_locked():判斷指定的自旋鎖是否已經(jīng)被獲取了。如果是則返回非0,否則,返回0。
? 釋放鎖
同獲取鎖相對(duì)應(yīng),內(nèi)核提供了三個(gè)相對(duì)的函數(shù)來(lái)釋放自旋鎖。
spin_unlock:釋放指定的自旋鎖。
spin_unlock_irq:釋放自旋鎖并激活本地中斷。
spin_unlock_irqsave:釋放自旋鎖,并恢復(fù)保存的本地中斷狀態(tài)。
五、讀寫(xiě)自旋鎖
如 果臨界區(qū)保護(hù)的數(shù)據(jù)是可讀可寫(xiě)的,那么只要沒(méi)有寫(xiě)操作,對(duì)于讀是可以支持并發(fā)操作的。對(duì)于這種只要求寫(xiě)操作是互斥的需求,如果還是使用自旋鎖顯然是無(wú)法滿(mǎn) 足這個(gè)要求(對(duì)于讀操作實(shí)在是太浪費(fèi)了)。為此內(nèi)核提供了另一種鎖-讀寫(xiě)自旋鎖,讀自旋鎖也叫共享自旋鎖,寫(xiě)自旋鎖也叫排他自旋鎖。
讀寫(xiě)自旋鎖是一種比自旋鎖粒度更小的鎖機(jī)制,它保留了“自旋”的概念,但是在寫(xiě)操作方面,只能最多有一個(gè)寫(xiě)進(jìn)程,在讀操作方面,同時(shí)可以有多個(gè)讀執(zhí)行單元,當(dāng)然,讀和寫(xiě)也不能同時(shí)進(jìn)行。
讀寫(xiě)自旋鎖的使用也普通自旋鎖的使用很類(lèi)似,首先要初始化讀寫(xiě)自旋鎖對(duì)象:
// 靜態(tài)初始化
rwlock_t rwlock = RW_LOCK_UNLOCKED;
//動(dòng)態(tài)初始化
rwlock_t *rwlock;
…
rw_lock_init(rwlock);
在讀操作代碼里對(duì)共享數(shù)據(jù)獲取讀自旋鎖:
read_lock(&rwlock);
…
read_unlock(&rwlock);
在寫(xiě)操作代碼里為共享數(shù)據(jù)獲取寫(xiě)自旋鎖:
write_lock(&rwlock);
…
write_unlock(&rwlock);
需要注意的是,如果有大量的寫(xiě)操作,會(huì)使寫(xiě)操作自旋在寫(xiě)自旋鎖上而處于寫(xiě)?zhàn)囸I狀態(tài)(等待讀自旋鎖的全部釋放),因?yàn)樽x自旋鎖會(huì)自由的獲取讀自旋鎖。
讀寫(xiě)自旋鎖的函數(shù)類(lèi)似于普通自旋鎖,這里就不一一介紹了,我們把它列在下面的表中。
RW_LOCK_UNLOCKED
rw_lock_init(rwlock_t *)
read_lock(rwlock_t *)
read_unlock(rwlock_t *)
read_lock_irq(rwlock_t *)
read_unlock_irq(rwlock_t *)
read_lock_irqsave(rwlock_t *, unsigned long)
read_unlock_irqsave(rwlock_t *, unsigned long)
write_lock(rwlock_t *)
write_unlock(rwlock_t *)
write_lock_irq(rwlock_t *)
write_unlock_irq(rwlock_t *)
write_lock_irqsave(rwlock_t *, unsigned long)
write_unlock_irqsave(rwlock_t *, unsigned long)
rw_is_locked(rwlock_t *)
六、順序瑣
順序瑣(seqlock)是對(duì)讀寫(xiě)鎖的一種優(yōu)化,若使用順序瑣,讀執(zhí)行單元絕不會(huì)被寫(xiě)執(zhí)行單元阻塞,也就是說(shuō),讀執(zhí)行單元可以在寫(xiě)執(zhí)行單元對(duì)被順序瑣保護(hù)的共享資源進(jìn)行寫(xiě)操作時(shí)仍然可以繼續(xù)讀,而不必等待寫(xiě)執(zhí)行單元完成寫(xiě)操作,寫(xiě)執(zhí)行單元也不需要等待所有讀執(zhí)行單元完成讀操作才去進(jìn)行寫(xiě)操作。
但是,寫(xiě)執(zhí)行單元與寫(xiě)執(zhí)行單元之間仍然是互斥的,即如果有寫(xiě)執(zhí)行單元在進(jìn)行寫(xiě)操作,其它寫(xiě)執(zhí)行單元必須自旋在哪里,直到寫(xiě)執(zhí)行單元釋放了順序瑣。
如果讀執(zhí)行單元在讀操作期間,寫(xiě)執(zhí)行單元已經(jīng)發(fā)生了寫(xiě)操作,那么,讀執(zhí)行單元必須重新讀取數(shù)據(jù),以便確保得到的數(shù)據(jù)是完整的,這種鎖在讀寫(xiě)同時(shí)進(jìn)行的概率比較小時(shí),性能是非常好的,而且它允許讀寫(xiě)同時(shí)進(jìn)行,因而更大的提高了并發(fā)性,
注意,順序瑣由一個(gè)限制,就是它必須被保護(hù)的共享資源不含有指針,因?yàn)閷?xiě)執(zhí)行單元可能使得指針失效,但讀執(zhí)行單元如果正要訪(fǎng)問(wèn)該指針,將導(dǎo)致Oops。
七、信號(hào)量
Linux中的信號(hào)量是一種睡眠鎖,如果有一個(gè)任務(wù)試圖獲得一個(gè)已經(jīng)被占用的信號(hào)量時(shí),信號(hào)量會(huì)將其推進(jìn)一個(gè)等待隊(duì)列,然后讓其睡眠,這時(shí)處理器能重獲自由,從而去執(zhí)行其它代碼,當(dāng)持有信號(hào)量的進(jìn)程將信號(hào)量釋放后,處于等待隊(duì)列中的哪個(gè)任務(wù)被喚醒,并獲得該信號(hào)量。
信號(hào)量,或旗標(biāo),就是我們?cè)诓僮飨到y(tǒng)里學(xué)習(xí)的經(jīng)典的P/V原語(yǔ)操作。
P:如果信號(hào)量值大于0,則遞減信號(hào)量的值,程序繼續(xù)執(zhí)行,否則,睡眠等待信號(hào)量大于0。
V:遞增信號(hào)量的值,如果遞增的信號(hào)量的值大于0,則喚醒等待的進(jìn)程。
信號(hào)量的值確定了同時(shí)可以有多少個(gè)進(jìn)程可以同時(shí)進(jìn)入臨界區(qū),如果信號(hào)量的初始值始1,這信號(hào)量就是互斥信號(hào)量(MUTEX)。對(duì)于大于1的非0值信號(hào)量,也可稱(chēng)為計(jì)數(shù)信號(hào)量(counting semaphore)。對(duì)于一般的驅(qū)動(dòng)程序使用的信號(hào)量都是互斥信號(hào)量。
類(lèi)似于自旋鎖,信號(hào)量的實(shí)現(xiàn)也與體系結(jié)構(gòu)密切相關(guān),具體的實(shí)現(xiàn)定義在頭文件中,對(duì)于x86_32系統(tǒng)來(lái)說(shuō),它的定義如下:
struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
};
信號(hào)量的初始值count是atomic_t類(lèi)型的,這是一個(gè)原子操作類(lèi)型,它也是一個(gè)內(nèi)核同步技術(shù),可見(jiàn)信號(hào)量是基于原子操作的。我們會(huì)在后面原子操作部分對(duì)原子操作做詳細(xì)介紹。
信號(hào)量的使用類(lèi)似于自旋鎖,包括創(chuàng)建、獲取和釋放。我們還是來(lái)先展示信號(hào)量的基本使用形式:
static DECLARE_MUTEX(my_sem);
……
if (down_interruptible(&my_sem))
{
return -ERESTARTSYS;
}
……
up(&my_sem)
Linux內(nèi)核中的信號(hào)量函數(shù)接口如下:
static DECLARE_SEMAPHORE_GENERIC(name, count);
static DECLARE_MUTEX(name);
seam_init(struct semaphore *, int);
init_MUTEX(struct semaphore *);
init_MUTEX_LOCKED(struct semaphore *)
down_interruptible(struct semaphore *);
down(struct semaphore *)
down_trylock(struct semaphore *)
up(struct semaphore *)
? 初始化信號(hào)量
信號(hào)量的初始化包括靜態(tài)初始化和動(dòng)態(tài)初始化。靜態(tài)初始化用于靜態(tài)的聲明并初始化信號(hào)量。
static DECLARE_SEMAPHORE_GENERIC(name, count);
static DECLARE_MUTEX(name);
對(duì)于動(dòng)態(tài)聲明或創(chuàng)建的信號(hào)量,可以使用如下函數(shù)進(jìn)行初始化:
seam_init(sem, count);
init_MUTEX(sem);
init_MUTEX_LOCKED(struct semaphore *)
顯然,帶有MUTEX的函數(shù)始初始化互斥信號(hào)量。LOCKED則初始化信號(hào)量為鎖狀態(tài)。
? 使用信號(hào)量
信號(hào)量初始化完成后我們就可以使用它了
down_interruptible(struct semaphore *);
down(struct semaphore *)
down_trylock(struct semaphore *)
up(struct semaphore *)
down函數(shù)會(huì)嘗試獲取指定的信號(hào)量,如果信號(hào)量已經(jīng)被使用了,則進(jìn)程進(jìn)入不可中斷的睡眠狀態(tài)。down_interruptible則會(huì)使進(jìn)程進(jìn)入可中斷的睡眠狀態(tài)。關(guān)于進(jìn)程狀態(tài)的詳細(xì)細(xì)節(jié),我們?cè)趦?nèi)核的進(jìn)程管理里在做詳細(xì)介紹。
down_trylock嘗試獲取信號(hào)量, 如果獲取成功則返回0,失敗則會(huì)立即返回非0。
當(dāng)退出臨界區(qū)時(shí)使用up函數(shù)釋放信號(hào)量,如果信號(hào)量上的睡眠隊(duì)列不為空,則喚醒其中一個(gè)等待進(jìn)程。
八、讀寫(xiě)信號(hào)量
類(lèi)似于自旋鎖,信號(hào)量也有讀寫(xiě)信號(hào)量。讀寫(xiě)信號(hào)量API定義在頭文件中,它的定義其實(shí)也是體系結(jié)構(gòu)相關(guān)的,因此具體實(shí)現(xiàn)定義在頭文件中,以下是x86的例子:
struct rw_semaphore {
signed long count;
spinlock_t wait_lock;
struct list_head wait_list;
};
linux工作隊(duì)列實(shí)現(xiàn)機(jī)制的介紹就聊到這里吧,感謝你花時(shí)間閱讀本站內(nèi)容,更多關(guān)于linux工作隊(duì)列實(shí)現(xiàn)機(jī)制,深入解析Linux工作隊(duì)列實(shí)現(xiàn)機(jī)制,《Linux設(shè)備驅(qū)動(dòng)程序》(十六)-中斷處理,如何實(shí)現(xiàn)linux下多線(xiàn)程之間的互斥與同步的信息別忘了在本站進(jìn)行查找喔。
成都服務(wù)器租用選創(chuàng)新互聯(lián),先試用再開(kāi)通。
創(chuàng)新互聯(lián)(www.cdcxhl.com)提供簡(jiǎn)單好用,價(jià)格厚道的香港/美國(guó)云服務(wù)器和獨(dú)立服務(wù)器。物理服務(wù)器托管租用:四川成都、綿陽(yáng)、重慶、貴陽(yáng)機(jī)房服務(wù)器托管租用。
當(dāng)前標(biāo)題:深入解析Linux工作隊(duì)列實(shí)現(xiàn)機(jī)制 (linux工作隊(duì)列實(shí)現(xiàn)機(jī)制)
轉(zhuǎn)載來(lái)源:http://m.fisionsoft.com.cn/article/dhieipo.html


咨詢(xún)
建站咨詢(xún)
