FreeRTOS的信號量包括二進制信號量、計數信號量、相互排斥信號量(以后簡稱相互排斥量)和遞歸相互排斥信號量(以后簡稱遞歸相互排斥量)。我們能夠把相互排斥量和遞歸相互排斥量看成特殊的信號量。
信號量API函數實際上都是宏。它使用現有的隊列機制。這些宏定義在semphr.h文件里。假設使用信號量或者相互排斥量。須要包括semphr.h頭文件。
二進制信號量、計數信號量和相互排斥量信號量的創建API函數是獨立的,可是獲取和釋放API函數都是同樣的;遞歸相互排斥信號量的創建、獲取和釋放API函數都是獨立的。
1創建二進制信號量
1.1函數描寫敘述
SemaphoreHandle_t xSemaphoreCreateBinary( void );
這個函數用於創建一個二進制信號量。二進制信號量要么有效要么無效。這也是為什么叫做二進制的原因。
新創建的信號量處於無效狀態。這意味着使用API函數xSemaphoreTake()獲取信號之前,須要先給出信號。
二進制信號量和相互排斥量非常類似,但也有細微的差別:相互排斥量具有優先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用於同步(任務之間或者任務和中斷之間),相互排斥量更適合互鎖。
一旦獲得二進制信號量后不須要恢復,一個任務或中斷不斷的產生信號。而還有一個任務不斷的取走這個信號,通過這種方式來實現同步。
低優先級任務擁有相互排斥量的時候,假設還有一個高優先級任務也企圖獲取這個信號量,則低優先級任務的優先級會被暫時提高,提高到和高優先級任務同樣的優先級。這意味着相互排斥量必須要釋放,否則高優先級任務將不能獲取這個相互排斥量,而且那個擁有相互排斥量的低優先級任務也永遠不會被剝奪,這就是操作系統中的優先級翻轉。
相互排斥量和二進制信號量都是SemaphoreHandle_t類型。而且能夠用於不論什么具有這類參數的API函數中。
1.1.2返回值
- NULL:創建信號量失敗。由於FreeRTOS堆棧不足。
- 其他值:信號量創建成功。
這個返回值存儲着信號量句柄。
1.1.3使用方法舉例
SemaphoreHandle_t xSemaphore; void vATask( void * pvParameters ) { /* 創建信號量 */ xSemaphore = xSemaphoreCreateBinary(); if( xSemaphore == NULL ) { /* 因堆棧不足,信號量創建失敗。這里進行失敗處理*/ } else { /* 信號量能夠使用。信號量句柄存儲在變量xSemahore中。 假設在這里調用API函數xSemahoreTake()來獲取信號量。 則必定是失敗的,由於創建的信號量初始是無效(空)的。*/ } }
2創建計數信號量
2.1函數描寫敘述
SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
創建計數信號量。計數信號量通經常使用於以下兩種情況:
- 事件計數:在這種應用場合,每當事件發生,事件處理程序會“產生”一個信號量(信號量計數值會遞增)。每當處理任務處理事件,會取走一個信號量(信號量計數值會遞減)。因此,事件發生或者事件被處理后。計數值是會變化的。
- 資源管理:在這種應用場合下,計數值表示有效資源的數目。為了獲得資源,任務首先要獲得一個信號量---遞減信號量計數值。當計數值為0時,表示沒有可用的資源。
當占有資源的任務完畢。它會釋放這個資源,對應的信號量計數值會增一。計數值達到初始值(最大值)表示所有資源都可用。
2.2參數描寫敘述
- uxMaxCount:最大計數值,當信號到達這個值后,就不再增長了。
- uxInitialCount:創建信號量時的初始值。
2.3返回值
NULL表示信號量創建失敗。否則返回信號量句柄。
2.4使用方法舉例
void vATask( void * pvParameters ) { xSemaphoreHandle xSemaphore; // 必須先創建信號量。才干使用它 // 信號量能夠計數的最大值為10,計數初始值為0. xSemaphore = xSemaphoreCreateCounting( 10, 0 ); if( xSemaphore != NULL ) { // 信號量創建成功 // 如今能夠使用信號量了。} }
3創建相互排斥量
3.1函數描寫敘述
SemaphoreHandle_t xSemaphoreCreateMutex( void )
創建相互排斥量。能夠使用API函數xSemaphoreTake()和xSemaphoreGive()訪問相互排斥量,可是絕不能夠用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()訪問。
二進制信號量和相互排斥量非常類似,但也有細微的差別:相互排斥量具有優先級繼承機制。二進制信號量沒有這個機制。
這使得二進制信號量更適合用於同步(任務之間或者任務和中斷之間)。相互排斥量更適合互鎖。
一旦獲得二進制信號量后不須要恢復。一個任務或中斷不斷的產生信號,而還有一個任務不斷的取走這個信號,通過這種方式來實現同步。
低優先級任務擁有相互排斥量的時候,假設還有一個高優先級任務也企圖獲取這個信號量,則低優先級任務的優先級會被暫時提高,提高到和高優先級任務同樣的優先級。這意味着相互排斥量必須要釋放,否則高優先級任務將不能獲取這個相互排斥量。而且那個擁有相互排斥量的低優先級任務也永遠不會被剝奪,這就是操作系統中的優先級翻轉。
相互排斥量和二進制信號量都是SemaphoreHandle_t類型。而且能夠用於不論什么具有這類參數的API函數中。
3.2返回值
NULL表示信號量創建失敗,否則返回信號量句柄。
3.3使用方法舉例
xSemaphoreHandle xSemaphore; voidvATask( void * pvParameters ) { // 相互排斥量在未創建之前是不可用的 xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { // 創建成功 // 在這里能夠使用這個相互排斥量了 } }
4創建遞歸相互排斥量
4.1函數描寫敘述
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
用於創建遞歸相互排斥量。被創建的相互排斥量能夠被API函數xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()使用,但不能夠被API函數xSemaphoreTake()和xSemaphoreGive()使用。
遞歸類型的相互排斥量能夠被擁有者反復獲取。擁有相互排斥量的任務必須調用API函數xSemaphoreGiveRecursive()將擁有的遞歸相互排斥量所有釋放后,該信號量才真正被釋放。比方,一個任務成功獲取同一個相互排斥量5次,那么這個任務要將這個相互排斥量釋放5次之后。其他任務才干獲取到它。
遞歸相互排斥量具有優先級繼承機制,因此任務獲得一次信號后必須在使用完后做一個釋放操作。
相互排斥量類型信號不能夠用在中斷服務例程中。
4.2返回值
NULL表示相互排斥量創建失敗,否則返回相互排斥量句柄。
4.3使用方法舉例
xSemaphoreHandle xMutex; void vATask( void * pvParameters ) { // 相互排斥量未創建前是不能被使用的 xMutex = xSemaphoreCreateRecursiveMutex(); if( xMutex != NULL ) { // 創建成功 // 在這里創建相互排斥量 } }
5刪除信號量
5.1函數描寫敘述
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
刪除信號量。
假設有任務堵塞在這個信號量上,則這個信號量不要刪除。
5.2參數描寫敘述
xSemaphore:信號量句柄
6獲取信號量
6.1函數描寫敘述
xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)
獲取信號量。信號量必須是通過API函數xSemaphoreCreateBinary()、xSemaphoreCreateCounting()和xSemaphoreCreateMutex()預先創建過的。注意,遞歸相互排斥量類型信號量不能使用該函數、不用在中斷服務程序中使用該函數。
6.2參數描寫敘述
- xSemaphore:信號量句柄
- xTickToWait:信號量無效時。任務最多等待的時間,單位是系統節拍周期個數。使用宏portTICK_PERIOD_MS能夠輔助將系統節拍個數轉化為實際時間(以毫秒為單位)。
假設設置為0。表示不是設置等待時間。假設INCLUDE_vTaskSuspend設置為1。而且參數xTickToWait為portMAX_DELAY則能夠無限等待。
6.3返回值
成功獲取到信號量返回pdTRUE,否則返回pdFALSE。
6.4使用方法舉例
SemaphoreHandle_t xSemaphore = NULL; /*這個任務創建信號量 */ void vATask( void * pvParameters ) { /*創建相互排斥型信號量,用於保護共享資源。*/ xSemaphore = xSemaphoreCreateMutex(); } /* 這個任務使用信號量 */ void vAnotherTask( void * pvParameters ) { /* ... 做其他事情. */ if( xSemaphore != NULL ) { /*假設信號量無效,則最多等待10個系統節拍周期。*/ if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) { /*到這里我們獲取到信號量。如今能夠訪問共享資源了*/ /* ... */ /* 完畢訪問共享資源后,必須釋放信號量*/ xSemaphoreGive( xSemaphore ); } else { /* 沒有獲取到信號量,這里處理異常情況。*/ } } }
7獲取信號量(帶中斷保護)
7.1函數描寫敘述
xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, signedBaseType_t *pxHigherPriorityTaskWoken)
API函數xSemaphoreTake()的還有一版本號,用於中斷服務程序。
7.2參數描寫敘述
- xSemaphore:信號量句柄
- pxHigherPriorityTaskWoken:假設*pxHigherPriorityTaskWoken為pdTRUE,則須要在中斷退出前手動進行一次上下文切換。從FreeRTOS V7.3.0開始。該參數為可選參數,並能夠設置為NULL。
7.3返回值
信號量成功獲取返回pdTRUE。否則返回pdFALSE。
8獲取遞歸相互排斥量
8.1函數描寫敘述
xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );
獲取遞歸相互排斥信號量。
相互排斥量必須是通過API函數xSemaphoreCreateRecursiveMutex()創建的類型。
文件FreeRTOSConfig.h中的宏configUSE_RECURSIVE_MUTEXES必須設置成1,此函數才有效。
已經獲取遞歸相互排斥量的任務能夠反復獲取該遞歸相互排斥量。使用xSemaphoreTakeRecursive() 函數成功獲取幾次遞歸相互排斥量。就要使用xSemaphoreGiveRecursive()函數返還幾次,在此之前遞歸相互排斥量都處於無效狀態。
比方。某個任務成功獲取5次遞歸相互排斥量,那么在它沒有返還5次該遞歸相互排斥量之前,這個相互排斥量對別的任務無效。
8.2參數描寫敘述
- xMutex:相互排斥量句柄。必須是使用API函數xSemaphoreCreateRecursiveMutex()返回的。
- xTickToWait:相互排斥量無效時,任務最多等待的時間,單位是系統節拍周期個數。使用宏portTICK_PERIOD_MS能夠輔助將系統節拍個數轉化為實際時間(以毫秒為單位)。假設設置為0。表示不是設置等待時間。假設任務已經擁有信號量則xSemaphoreTakeRecursive()馬上返回,無論xTickToWait是什么值。
8.3返回值
成功獲取遞歸相互排斥量返回pdTURE,否則返回pdFALSE。
8.4使用方法舉例
SemaphoreHandle_t xMutex = NULL; // 這個任務創建相互排斥量 void vATask( void *pvParameters ) { // 這個相互排斥量用於保護共享資源 xMutex =xSemaphoreCreateRecursiveMutex(); } //這個任務使用相互排斥量 void vAnotherTask( void *pvParameters ) { // ... 做其他事情. if( xMutex != NULL ) { // 假設相互排斥量無效,則最多等待10系統時鍾節拍周期. if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) { // 到這里我們成功獲取相互排斥量並能夠訪問共享資源了 // ... // 由於某種原因,某些代碼須要在一個任務中多次調用API函數 // xSemaphoreTakeRecursive()。當然不會像本例中這樣連續式 //調用。實際代碼會有更加復雜的結構 xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // 我們獲取一個相互排斥量三次。所以我們要將這個相互排斥量釋放三次 //它才會變得有效。再一次說明,實際代碼可能會更加復雜。 xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); // 到這里,這個共享資源能夠被其他任務使用了. } else { // 處理異常情況 } } }
9釋放信號量
9.1函數描寫敘述
xSemaphoreGive(SemaphoreHandle_t xSemaphore )
用於釋放一個信號量。信號量必須是API函數xSemaphoreCreateBinary()、xSemaphoreCreateCounting()或xSemaphoreCreateMutex() 創建的。必須使用API函數xSemaphoreTake()獲取這個信號量。
這個函數絕不能夠在中斷服務例程中使用,能夠使用帶中斷保護版本號的API函數xSemaphoreGiveFromISR()來實現同樣功能。
這個函數不能用於使用API函數xSemaphoreCreateRecursiveMutex()所創建的遞歸相互排斥量。
9.2參數描寫敘述
- xSemaphore:信號量句柄。
9.3返回值
信號量釋放成功返回pdTRUE。否則返回pdFALSE。
9.4使用方法舉例
SemaphoreHandle_t xSemaphore = NULL; voidvATask( void * pvParameters ) { // 創建一個相互排斥量,用來保護共享資源 xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { //我們希望這個函數調用失敗。由於首先要獲取相互排斥量 } // 獲取信號量,不等待 if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) { // 如今我們擁有相互排斥量,能夠安全的訪問共享資源 if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { //我們不希望這個函數調用失敗,由於我們必須 //要釋放已獲取的相互排斥量 } } } }
10釋放信號量(帶中斷保護)
10.1函數描寫敘述
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken )
釋放信號量。是API函數xSemaphoreGive()的另個版本號。用於中斷服務程序。
信號量必須是通過API函數xSemaphoreCreateBinary()或xSemaphoreCreateCounting()創建的。這里沒有相互排斥量,是由於相互排斥量不能夠用在中斷服務程序中。
10.2參數描寫敘述
xSemaphore:信號量句柄
pxHigherPriorityTaskWoken:假設*pxHigherPriorityTaskWoken為pdTRUE,則須要在中斷退出前人為的經行一次上下文切換。
從FreeRTOS V7.3.0開始,該參數為可選參數,並能夠設置為NULL。
10.3返回值
成功釋放信號量返回pdTURE,否則返回errQUEUE_FULL。
10.4使用方法舉例
#define LONG_TIME 0xffff #define TICKS_TO_WAIT 10 SemaphoreHandle_t xSemaphore = NULL; /* Repetitive task. */ void vATask( void * pvParameters ) { /* 我們使用信號量同步,所以先創建一個二進制信號量.必須確保 在創建這個二進制信號量之前。中斷不會訪問它。*/ xSemaphore = xSemaphoreCreateBinary(); for( ;; ) { /* 我們希望每產生10次定時器中斷。任務執行一次。*/ if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) { /* 到這里成功獲取到信號量*/ ... /* 我們成功執行完一次。由於這是個死循環,所以任務仍會 堵塞在等待信號量上。信號量由ISR釋放。*/ } } } /* 定時器 ISR */ void vTimerISR( void * pvParameters ) { static unsigned char ucLocalTickCount = 0; static signed BaseType_txHigherPriorityTaskWoken; /*定時器中斷發生 */ ...執行其他代碼 /*須要vATask() 執行嗎?
*/ xHigherPriorityTaskWoken = pdFALSE; ucLocalTickCount++; if( ucLocalTickCount >= TICKS_TO_WAIT ) { /* 釋放信號量,解除vATask任務堵塞狀態 */ xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); /* 復位計數器 */ ucLocalTickCount = 0; } /* 假設 xHigherPriorityTaskWoken 表達式為真,須要執行一次上下文切換*/ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
11釋放遞歸相互排斥量
11.1函數描寫敘述
xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )
釋放一個遞歸相互排斥量。相互排斥量必須是使用 API函數xSemaphoreCreateRecursiveMutex()創建的。文件FreeRTOSConfig.h中宏configUSE_RECURSIVE_MUTEXES必須設置成1本函數才有效。
11.2參數描寫敘述
- xMutex:相互排斥量句柄。必須是函數xSemaphoreCreateRecursiveMutex()返回的值。
11.3返回值
假設遞歸相互排斥量釋放成功。返回pdTRUE。
11.4使用方法舉例
見“8 獲取遞歸相互排斥量”。
12獲取相互排斥量持有任務的句柄
12.1函數描寫敘述
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
返回相互排斥量持有任務的句柄(假設有的話),相互排斥量由參數xMutex指定。
假設調用此函數的任務持有相互排斥量,那么能夠可靠的返回任務句柄。可是假設是別的任務持有相互排斥量,則不總可靠。
文件FreeRTOSConfig.h中宏configUSE_MUTEXES必須設置成1本函數才有效。
12.2參數描寫敘述
- xMutex:相互排斥量句柄
12.3返回值
返回相互排斥量持有任務的句柄。
假設參數xMutex不是相互排斥類型信號量或者盡管相互排斥量有效但這個相互排斥量不被不論什么任務持有則返回NULL。
這是FreeRTOS基礎篇的最后一篇博文,到這里我們已經能夠移植、熟練使用FreeRTOS了。但假設想知道FreeRTOS背后的執行機制,這些是遠遠不夠的,以下要走的路還會非常長。
要不要了解FreeRTOS背后執行機制。全憑各位的興趣。畢竟我們即使不清楚汽車的構造細節,但僅僅要掌握駕駛技巧也能夠非常好的開車的。使用RTOS也與之類似。僅僅要我們掌握了基礎篇的那些知識。我們已經能夠非常好的使用FreeRTOS了。
探索FreeRTOS背后執行的機制,是我們對未知事件的好奇。也是我們相信理解了FreeRTOS執行機制,能夠讓我們更優雅、更少犯錯、更舉重若輕的的使用RTOS。
FreeRTOS高級篇已經開始寫了。能夠點擊這里擦看最新的文章列表。