信號量使用小結


信號量

1、信號量簡介

    信號量又稱為信號燈,它是用來協調不同進程間的數據對象的,而最主要的應用是共享內存方式的進程間通信。本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取狀況。一般說來,為了獲得共享資源,進程需要執行下列操作:
   (1) 測試控制該資源的信號量。
   (2) 若此信號量的值為正,則允許進行使用該資源。進程將信號量減1。
   (3) 若此信號量為0,則該資源目前不可用,進程進入睡眠狀態,直至信號量值大於0,進程被喚醒,轉入步驟(1)。
   (4) 當進程不再使用一個信號量控制的資源時,信號量值加1。如果此時有進程正在睡眠等待此信號量,則喚醒此進程。
    維護信號量狀態的是Linux內核操作系統而不是用戶進程。我們可以從頭文件/usr/src/linux/include/linux/sem.h 中看到內核用來維護信號量狀態的各個結構的定義。信號量是一個數據集合,用戶可以單獨使用這一集合的每個元素。要調用的第一個函數是semget,用以獲得一個信號量ID。Linux2.6.26下定義的信號量結構體:

struct semaphore {

        spinlock_t                lock;

        unsigned int             count;

        struct list_head        wait_list;

};

從以上信號量的定義中,可以看到信號量底層使用到了spin lock的鎖定機制,這個spinlock主要用來確保對count成員的原子性的操作(count--)和測試(count > 0)。

    信號量(semaphore )實際是一個整數,它的值由多個進程進行測試(test)和設置(set)。就每個進程所關心的測試和設置操作而言,這兩個操作是不可中斷的,或稱“原子”操作,即一旦開始直到兩個操作全部完成。測試和設置操作的結果是:信號量的當前值和設置值相加,其和或者是正或者為負。根據測試和設置操作的結果,一個進程可能必須睡眠,直到有另一個進程改變信號量的值。

    信號量可用來實現所謂的“臨界區”的互斥使用,臨界區指同一時刻只能有一個進程執行其中代碼的代碼段。為了進一步理解信號量的使用,下面我們舉例說明。

     假設你有很多相互協作的進程,它們正在讀或寫一個數據文件中的記錄。你可能希望嚴格協調對這個文件的存取,於是你使用初始值為1的信號量,在這個信號量上實施兩個操作,首先測試並且給信號量的值減1,然后測試並給信號量的值加1。當第一個進程存取文件時,它把信號量的值減1,並獲得成功,信號量的值現在變為0,這個進程可以繼續執行並存取數據文件。但是,如果另外一個進程也希望存取這個文件,那么它也把信號量的值減1,結果是不能存取這個文件,因為信號量的值變為-1。這個進程將被掛起,直到第一個進程完成對數據文件的存取。當第一個進程完成對數據文件的存取,它將增加信號量的值,使它重新變為1,現在,等待的進程被喚醒,它對信號量的減1操作將獲得成功。

上述的進程互斥問題,是針對進程之間要共享一個臨界資源而言的,信號量的初值為1。實際上,信號量作為資源計數器,它的初值可以是任何正整數,其初值不一定為0或1。另外,如果一個進程要先獲得兩個或多個的共享資源后才能執行的話,那么,相應地也需要多個信號量,而多個進程要分別獲得多個臨界資源后方能運行,這就是信號量集合機制,Linux 討論的就是信號量集合問題。

    進程間對共享資源的互斥訪問是通過“信號量”機制來實現的。信號量機制是操作系統教材中比較重要的內容之一。Linux內核中提供了兩個函數down()和up(),分別對應於操作系統教材中的P、V操作。

信號量在內核中定義為semaphore數據結構,其位於include/i386/semaphore.h:

c代碼

struct semaphore {
         atomic_t count;
             int sleepers;
         wait_queue_head_t wait;
 #if WAITQUEUE_DEBUG
         long __magic;
 #endif
 };

    其中的count域就是“信號量”中的那個“量”,它代表着可用資源的數量。如果該值大於0,那么資源就是空閑的,也就是說,該資源可以使用。相反,如果count小於0,那么這個信號量就是繁忙的,也就是說,這個受保護的資源現在不能使用。在后一種情況下,count的絕對值表示了正在等待這個資源的進程數。該值為0表示有一個進程正在使用這個資源,但沒有其他進程在等待這個資源。

   Wait域存放等待鏈表的地址,該鏈表中包含正在等待這個資源的所有睡眠的進程。當然,如果count大於或等於0,則等待隊列為空。為了明確表示等待隊列中正在等待的進程數,引入了計數器sleepers。

down()和up()函數主要應用在文件系統和驅動程序中,把要保護的臨界區放在這兩個函數中間,用法如下:

down();

臨界區

up();

2、信號量的使用及多任務的互斥

2.1信號量使用

(1)二進制信號量

    二進制信號量只能有一個值,0或1。當一個二進制信號量的值為0時,認為信號量是不可使用的或空的(empty);當值為1時,認為信號量時可用的或滿的(full),使用二進制信號量能夠滿足兩種任務的協調需要:互斥和同步。二進制信號量需要的系統開銷最小,因而特別是用於高性能的需求。在“互斥信號量”中討論的互斥信號量也是一種二進制信號量,但是它用於解決內在互斥的問題。在不需要使用互斥信號量的高級特性時。二進制信號量仍可用於互斥。

    二進制信號量的特點如下:

1、  不支持嵌套;

2、  不支持優先級繼承協議;

3  沒有任務刪除保護。

因此,在只用到互斥的場合,使用互斥信號量更可取。

(2)互斥信號量

互斥二進制信號量是一種用於解決內在互斥問題的特殊二進制信號量,包括優先級倒置、刪除安全以及資源的遞歸訪問。

互斥信號量的基本行為與二進制信號量一致,不同之處如下:

1、  他僅用於互斥;

2、  它僅能由提取它(即調用semTake())的任務釋放;

3、不能在中斷服務程序中釋放;

4、semFlush()函數操作非法。

(3)計數器信號量

計數器信號量使用一個計數器,允許多次獲取和釋放,用於控制系統中共享資源的多個任務的使用,是實現任務同步和互斥的另一個手段。

    每釋放一個信號量,計數器加一。每提取一個信號量,計計數器減一。計數器為0時,試圖提取信號量的任務將被阻塞。與二進制信號量不同的是,如果信號量被釋放時不存在阻塞任務,那么計數器加一。這意味着一個被釋放兩次的信號量,可以無阻賽的被提取兩次。

2.2多任務互斥

(1)任務調度

基於優先級的調度算法:WIND內核分為256個優先級,編號為0~255,優先級0最高,255最低;我們系統的優先級與之相反,數值越大,優先級越高。

時間片輪調法:所有ready態的任務平均占用系統時間,防止某一高優先級的任務長期占用資源而導致其他任務無法運行。

我們使用時通常將兩種調度算法結合起來。

(2)鎖任務切換

關閉任務切換功能,即在此任務運行過程中,即使有更高優先級的任務也不會發生任務切換。使用函數為taskLock和taskUnlock。

在鎖任務切換過程中,如果此任務掛起或阻塞,則系統從已就緒的任務中選擇優先級最高的任務執行,一旦遠任務恢復,則鎖任務切換繼續有效。

(3)中斷上鎖

這種方法涉及中斷級互斥,也就是說,在互斥期間,即使外部事件的產生而引發相應的中斷,系統也不會切換到相應的中斷服務程序(ISR)。從而在上鎖期間,他可能會造成系統對外部事件反應遲鈍。這對於大多數實時系統而言,系統的實時性也得不到保證,因而不適合作為一種通用的互斥方法。

然而,當涉及ISR需要互斥時,中斷上鎖又是必要的。但是任何情形下,應該使中斷上鎖時間盡量短,這也是所有操作系統的基本要求。

(4)刪除任務

刪除任務有兩種方式:exit()刪除任務本身,taskDelete()刪除指定的任務。這兩種刪除任務的只釋放所分配給任務的內存,如棧空間,而不會釋放任務執行過程中所申請的資源,如malloc所分配的內存,申請的信號量等,因此在刪除任務前必須確保此任務運行過程中所分配的資源都已釋放,否則會造成資源泄漏。

2.3使用信號量應該注意的問題

(1)信號量也是一種資源,當某些任務占用信號量沒有釋放,可能會導致別的任務因為沒有信號量可用而得不到執行,所以任務運行結束后必須釋放信號量。

(2)申請信號量VOS_SmP( VOS_UINT32 Sm_ID, VOS_UINT32 ulTimeOutInMillSec )第二個參數ulTimeOutInMillSec為等待時間(以毫秒為單位),即沒有可用信號量時此任務等待的時間,如果在此時間內沒有獲得可用的信號量,則任務繼續運行,並且不改變計數器的值,等待時間為0時表示永久等待。

(3)任務因為沒有信號量而被阻塞,恢復運行的方式有兩種:其他任務釋放信號量,此任務獲得信號量;或者超時后任務繼續運行。兩者沒有本質區別,前者任務獲得信號量后並沒有立即運行,而是任務進入ready態,放在同優先級隊列的尾部等待執行,而后者在超時后任務也進入ready態,放在同優先級隊列的尾部等待執行。

                        任務執行流圖

任務進入阻塞隊列前由任務自己控制,一旦進入阻塞對后的操作由系統負責

(4)等待時間一般不要設為0(永久等待),因為一旦出現異常,此任務就會被掛死,導致系統運行出錯。對於需要事件觸發的任務而言,等待時間可以設置為0,此時即使出現異常,也只是一次事件未被處理,而不會影響到整個系統的運行。

2.4多任務編程注意事項

(1)首先明確各個任務之間的通信過程,確定整個系統的通信流程;

(2)確定需要保護的資源;所有公共的,且每次只能由一個任務訪問的資源都需要進行保護;

(3)一個完整的通信過程不能被打斷,不但要對此通信所需的資源進行保護,整個通信過程也必須進行保護,以保證通信過程的完整性。對通信過程的保護可以使用信號量,也可以使用狀態機;

(4)需要注意:不同的任務擁有各自的棧空間,必須明確每一時刻只能有一個任務在運行,這個任務的所有局部變量使用自己的棧空間,和別的任務互不沖突;

(5)必須注意:當一個任務因沒有信號量而阻塞,另一個任務釋放信號量時,前一個任務獲得信號量后並不是立即就可以執行,而是放入就緒隊列中等待執行,因此多任務的執行順序是很復雜的過程,而不是簡單的按照程序設計的順序執行;

(6)信號量使用后必須釋放;

(7)合理使用狀態機控制整個通信過程,可以減少信號量的使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM