一、信號量控制塊:在include/rtdef.h中
#ifdef RT_USING_SEMAPHORE /** * Semaphore structure */ struct rt_semaphore { struct rt_ipc_object parent; /**< inherit from ipc_object *///派生自IPC對象 rt_uint16_t value; /**< value of semaphore. */ //信號量計數器 }; typedef struct rt_semaphore *rt_sem_t; #endif
信號量是用來解決線程同步和互斥的通用工具,和互斥量類似,信號量也可用作資源互斥訪問,但信號量沒有所有者的概念,在應用上比互斥量更廣泛。信號量比較簡單,不能解決優先級翻轉問題,但信號量是一種輕量級的對象,比互斥量小巧、靈活。因此在很多對互斥要求不嚴格的系統中(或者不會造成優先級翻轉的情況下),經常使用信號量來管理互斥資源。value為信號計數器,此信號量多次被釋放時將會累加,在被獲取時則將減1,當其值為0時,再請求獲取的線程將會被掛起到掛起鏈表中。
二、信號量相關接口:在src/ipc.c中
創建動態信號量: rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); 當調用這個函數時,系統將先分配一個semaphore對象,並初始化這個對象,然后初始化IPC對象以及與semaphore相關的部分。在創建信號量指定的參數中,信號量標志參數決定了當信號量不可用時,多個線程等待的排隊方式。當選擇FIFO方式時,那么等待線程隊列將按照先進先出的方式排隊,先進入的線程將先獲得等待的信號量;當選擇PRIO(優先級等待)方式時,等待線程隊列將按照優先級進行排隊,優先級高的等待線程將先獲得等待的信號量。 刪除動態信號量: rt_err_t rt_sem_delete(rt_sem_t sem); 調用這個函數時,系統將刪除這個信號量。如果刪除該信號量時,有線程正在等待該信號量,那么刪除操作會先喚醒所有等待在該信號量上的線程(所有等待線程的error返回值是-RT_ERROR,表明為異常喚醒),然后再釋放信號量的內存資源。
初始化靜態信號量: rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag); 當調用這個函數時,系統將對這個semaphore對象進行初始化,然后初始化IPC對象以及與semaphore相關的部分。在初始化信號量指定的參數中,信號量標志參數決定了當信號量不可用時,多個線程等待的方式。當選擇FIFO方式時,那么等待線程隊列將按照先進先出的方式排隊,先進入的線程將先獲得等待的信號量;當選擇PRIO(優先級等待)方式時,等待線程隊列將按照優先級進行排隊,優先級高的等待線程將先獲得等待的信號量。 脫離靜態信號量: rt_err_t rt_sem_detach(rt_sem_t sem); 使用該函數后,內核先喚醒所有掛在該信號量等待隊列上的所有線程,然后將該信號量從內核對象管理器中刪除。原來掛起在信號量上的所有等待線程error值將獲得-RT_ERROR 的返回值。
獲取信號量: rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time); 線程通過獲取信號量來獲得信號量資源實例,當信號量值大於零時,線程將獲得信號量,並且相應的信號量值都會減1。 在調用這個函數時,如果信號量的值等於零,那么說明當前信號量資源實例不可用,申請該信號量的線程將根據time參數的情況選擇直接返回、或掛起等待一段時間、或永久等待,直到其他線程或中斷釋放該信號量。如果在參數time指定的時間內依然得不到信號量,線程將超時返回,返回值是-RT_ETIMEOUT。
獲取信號量函數首先會判斷當前是否有信號量(通過value值是否大於0來判斷),如果有則立即成功返回,如果沒有,則接下來首先判斷是否有時間參數,如果等待時間參數為0,則表示需要立即返回,則立即返回錯誤。如果等待時間參數大於0,則表示需要延時一段時間,在此延時期間,如何信號量到達,或者信號量被非法脫離,或一直沒有等到,則通過判斷線程的error值來判斷當前是否已經成功獲得信號值,因為如果成功獲得信號量時,即在另一個線程release信號后,因此這一整個過程並沒有修改線程的error值,因此,線程的error值一直保持原先的RT_EOK不變。若是線程一直沒有等待到信號量的到達,即產生的定時器超時時(在take過程中會將設置一定時器,然后啟動它,再掛起當前線程),在線程定時器的回調超時處理函數中,程序會將線程的error值修改為-RT_ETIMEOUT。另,如果之前講解脫離線程時,如果在某一線程等待信號量期間,這個信號量被意外脫離了時,在脫離信號量的函數中,程序會將線程的error值修改為-RT_ERROR。綜上所述,程序可以通過線程的error值來對其是否真正獲得信號量進行判定,即如果線程的error值一直保持原樣即thread->error==RT_EOK時,則為已獲取信號量,否則沒有獲取,要么超時(-RT_ETIMEOUT),要么非法脫離(-RT_ERROR)了。
無等待獲取信號量: rt_err_t rt_sem_trytake(rt_sem_t sem); 當用戶不想在申請的信號量上掛起線程進行等待時,可以使用無等待方式獲取信號量,這個函數與rt_sem_take(sem, 0) 的作用相同,即當線程申請的信號量資源實例不可用的時候,它不會等待在該信號量上,而是直接返回-RT_ETIMEOUT。 釋放信號量: rt_err_t rt_sem_release(rt_sem_t sem); 當線程完成資源的訪問后,應盡快釋放它持有的信號量,使得其他線程能獲得該信號量。當信號量的值等於零時,並且有線程等待這個信號量時,將喚醒等待在該信號量線程隊列中的第一個線程,由它獲取信號量。否則將把信號量的值加1。
控制信號量: rt_err_t rt_sem_control(rt_sem_t sem, rt_uint8_t cmd, void *arg); 對於cmd,在rtdef.h中只定義了#define RT_IPC_CMD_RESET這一條有效命令,即重新設置信號量值value,同時喚醒所有等待該信號量的線程,參數arg的值為新的信號量值。
只支持重置信號量,此時若其存在掛起線程,則將其全部喚醒再次重新調度。此時在rt_ipc_list_resume_all函數中會將所有掛起的線程的error值設置為-RT_ERROR.