CMSIS-RTOS 信號量Semaphores


信號量Semaphores

和信號類似,信號量也是一種同步多個線程的方式,簡單來講,信號量就是裝有一些令牌的容器。當一個線程在執行過程中,就可能遇到一個系統調用來獲取信號量令牌,如果這個信號量包含多個令牌,線程就會繼續執行,同時信號量令牌的數量就會減一。如果此時信號量中沒有令牌,線程就會被置於等待狀態,直到出現一個可用的令牌。在線程執行的任何位置,它都可以給信號量增加一個令牌。

這里寫圖片描述

信號量用來幫助訪問程序資源,在一個線程允許訪問一個信號量之前,它必須擁有一個令牌。如果沒有令牌可用,它就必須等待,當線程使用完資源時,它就必須釋放令牌。

上圖揭示了兩個線程如何使信號量同步。首先,必須創建一個信號量,並初始化令牌數目,在上圖中,信號量初始化令牌數目為1。當兩個線程運行到某一點時就試圖從信號量中請求一個令牌,圖中第一個線程到達這個點,成功獲取一個令牌,然后繼續執行,第二個線程也試圖獲取一個令牌,但是當前信號量為空,所以它暫停執行,並進入等待狀態,直到信號量中有令牌可用。

與此同時,執行中的線程可以釋放令牌給信號量,一旦釋放完成,等待中的線程就會獲取令牌,並離開等待狀態進入准備狀態。緊接着調度器就會把它調度到運行狀態去執行剩下的代碼。

因為信號量保含較多的系統調用,所以想一次性全部理解有些難度,在本節,我們將首先看看如何給系統添加信號量,然后了解一下常用的信號量應用。

在使用信號量之前,你必須先聲明一個信號量容器:

osSemaphoreId sem1;
osSemaphoreDef(sem1);
  • 1
  • 2

然后在線程里給信號量容器初始化一些令牌:

sem1 = osSemaphoreCreate(osSemaphore(sem1), SIX_TOKENS);
  • 1

有一點比較重要,就是在線程運行的過程中令牌既可以被創建也可以被銷毀,舉個例子,你可以初始化一個信號量,擁有0個令牌,然后用一個線程給這個信號量創建一些令牌,再使用另一個線程移除它們,這樣一來,你就可以設計線程,既可以充當生產者的線程,也可以充當消費者的線程。

一旦信號量被創建,令牌就可能被獲取,並以類似事件標志的方式發送給信號量,os_sem_wait調用來阻塞線程,直到有信號量令牌可用,類似os_event_wait,當然,在這個調用中同樣擁有超時機制,超時初始值是0xFFFF。

osStatus osSemaphoreWait(osSemaphoreId semaphore_id, uint32_t millisec);
  • 1

一旦線程完成對信號量資源的使用,它就可以給信號量容器發送一個令牌:

osStatus osSemaphoreRelease(osSemaphoreId semaphore_id);
  • 1

練習:信號量的信號傳輸

在這個練習中,我們將看到如何配置一個信號量,並使用它在兩個任務間發送信號。

打開Pack Installer,選擇”Ex 9 Semaphore Signaling”,然后復制到你的指定路徑

首先創建一個信號量sem1,然后給它初始化0個令牌:

osSemaphoreId sem1;
osSemaphoreDef(sem1);
int main(void){ sem1 = osSemaphoreCreate(osSemaphore(sem1), 0);
  • 1
  • 2
  • 3
  • 4

第一個線程等待一個令牌被發送到信號量容器:

void led_Thread1(void const *argument){ for(;;){ osSemaphoreWait(sem1, osWaitForever); LED_On(1); osDelay(500); LED_Off(1); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

與此同時,第二個線程周期性的給信號量發送令牌:

void led_Thread2(void const *argument){ for(;;){ LED_On(2); osSemaphoreRelease(sem1); osDelay(500); LED_Off(2); osDelay(500); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

編譯工程並啟動仿真環境

在led_Thread2任務設置斷點:

這里寫圖片描述

運行代碼,運行到斷點,觀察線程狀態:

這里寫圖片描述

現在led_thread1被阻塞,等待從信號量獲取一個令牌,線程1的優先級比線程2高,所以一旦令牌放入信號量,線程1就會立即進入准備狀態,搶占低優先級線程,並隨后啟動運行。當運行到osSemaphoreWait()調用后,它又再次阻塞。

現在單步運行(F10),觀察線程及信號量的行為。


免責聲明!

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



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