FreeRTOS信號量API函數(shù)基礎(chǔ)教程
前言
FreeRTOS的信號量包括二進制信號量、計數(shù)信號量、互斥信號量(以后簡稱互斥量)和遞歸互斥信號量(以后簡稱遞歸互斥量)。我們可以把互斥量和遞歸互斥量看成特殊的信號量。
信號量API函數(shù)實際上都是宏,它使用現(xiàn)有的隊列機制。這些宏定義在semphr.h文件中。如果使用信號量或者互斥量,需要包含semphr.h頭文件。
二進制信號量、計數(shù)信號量和互斥量信號量的創(chuàng)建API函數(shù)是獨立的,但是獲取和釋放API函數(shù)都是相同的;遞歸互斥信號量的創(chuàng)建、獲取和釋放API函數(shù)都是獨立的。
1創(chuàng)建二進制信號量
1.1函數(shù)描述
SemaphoreHandle_t xSemaphoreCreateBinary( void );
這個函數(shù)用于創(chuàng)建一個二進制信號量。二進制信號量要么有效要么無效,這也是為什么叫做二進制的原因。
新創(chuàng)建的信號量處于無效狀態(tài),這意味著使用API函數(shù)xSemaphoreTake()獲取信號之前,需要先給出信號。
二進制信號量和互斥量非常相似,但也有細(xì)微的區(qū)別:互斥量具有優(yōu)先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用于同步(任務(wù)之間或者任務(wù)和中斷之間),互斥量更適合互鎖。
一旦獲得二進制信號量后不需要恢復(fù),一個任務(wù)或中斷不斷的產(chǎn)生信號,而另一個任務(wù)不斷的取走這個信號,通過這樣的方式來實現(xiàn)同步。
低優(yōu)先級任務(wù)擁有互斥量的時候,如果另一個高優(yōu)先級任務(wù)也企圖獲取這個信號量,則低優(yōu)先級任務(wù)的優(yōu)先級會被臨時提高,提高到和高優(yōu)先級任務(wù)相同的優(yōu)先級。這意味著互斥量必須要釋放,否則高優(yōu)先級任務(wù)將不能獲取這個互斥量,并且那個擁有互斥量的低優(yōu)先級任務(wù)也永遠(yuǎn)不會被剝奪,這就是操作系統(tǒng)中的優(yōu)先級翻轉(zhuǎn)。
互斥量和二進制信號量都是SemaphoreHandle_t類型,并且可以用于任何具有這類參數(shù)的API函數(shù)中。
1.2返回值
NULL:創(chuàng)建信號量失敗,因為FreeRTOS堆棧不足。其它值:信號量創(chuàng)建成功。這個返回值存儲著信號量句柄。
1.3用法舉例
SemaphoreHandle_t xSemaphore; void vATask( void * pvParameters ) { /* 創(chuàng)建信號量 */ xSemaphore = xSemaphoreCreateBinary(); if( xSemaphore == NULL ) { /* 因堆棧不足,信號量創(chuàng)建失敗,這里進行失敗處理*/ } else { /* 信號量可以使用。信號量句柄存儲在變量xSemahore中。 如果在這里調(diào)用API函數(shù)xSemahoreTake()來獲取信號量, 則必然是失敗的,因為創(chuàng)建的信號量初始是無效(空)的。*/ } }
2創(chuàng)建計數(shù)信號量
2.1函數(shù)描述
SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
創(chuàng)建計數(shù)信號量,計數(shù)信號量通常用于以下兩種情況:
事件計數(shù):在這種應(yīng)用場合,每當(dāng)事件發(fā)生,事件處理程序會“產(chǎn)生”一個信號量(信號量計數(shù)值會遞增),每當(dāng)處理任務(wù)處理事件,會取走一個信號量(信號量計數(shù)值會遞減)。因此,事件發(fā)生或者事件被處理后,計數(shù)值是會變化的。
資源管理:在這種應(yīng)用場合下,計數(shù)值表示有效資源的數(shù)目。為了獲得資源,任務(wù)首先要獲得一個信號量---遞減信號量計數(shù)值。當(dāng)計數(shù)值為0時,表示沒有可用的資源。當(dāng)占有資源的任務(wù)完成,它會釋放這個資源,相應(yīng)的信號量計數(shù)值會增一。計數(shù)值達(dá)到初始值(最大值)表示所有資源都可用。
2.2參數(shù)描述
uxMaxCount
:最大計數(shù)值,當(dāng)信號到達(dá)這個值后,就不再增長了。
uxInitialCount
:創(chuàng)建信號量時的初始值。
2.3返回值
NULL表示信號量創(chuàng)建失敗,否則返回信號量句柄。
2.4用法舉例
void vATask( void * pvParameters ) { xSemaphoreHandle xSemaphore; // 必須先創(chuàng)建信號量,才能使用它 // 信號量可以計數(shù)的最大值為10,計數(shù)初始值為0. xSemaphore = xSemaphoreCreateCounting( 10, 0 ); if( xSemaphore != NULL ) { // 信號量創(chuàng)建成功 // 現(xiàn)在可以使用信號量了。 } }
3創(chuàng)建互斥量
3.1函數(shù)描述
SemaphoreHandle_t xSemaphoreCreateMutex( void )
創(chuàng)建互斥量。
可以使用API函數(shù)xSemaphoreTake()和xSemaphoreGive()訪問互斥量,但是絕不可以用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()訪問。
二進制信號量和互斥量非常相似,但也有細(xì)微的區(qū)別:互斥量具有優(yōu)先級繼承機制,二進制信號量沒有這個機制。這使得二進制信號量更適合用于同步(任務(wù)之間或者任務(wù)和中斷之間),互斥量更適合互鎖。
一旦獲得二進制信號量后不需要恢復(fù),一個任務(wù)或中斷不斷的產(chǎn)生信號,而另一個任務(wù)不斷的取走這個信號,通過這樣的方式來實現(xiàn)同步。
低優(yōu)先級任務(wù)擁有互斥量的時候,如果另一個高優(yōu)先級任務(wù)也企圖獲取這個信號量,則低優(yōu)先級任務(wù)的優(yōu)先級會被臨時提高,提高到和高優(yōu)先級任務(wù)相同的優(yōu)先級。這意味著互斥量必須要釋放,否則高優(yōu)先級任務(wù)將不能獲取這個互斥量,并且那個擁有互斥量的低優(yōu)先級任務(wù)也永遠(yuǎn)不會被剝奪,這就是操作系統(tǒng)中的優(yōu)先級翻轉(zhuǎn)。
互斥量和二進制信號量都是SemaphoreHandle_t類型,并且可以用于任何具有這類參數(shù)的API函數(shù)中。
3.2返回值
NULL表示信號量創(chuàng)建失敗,否則返回信號量句柄。
3.3用法舉例
xSemaphoreHandle xSemaphore; voidvATask( void * pvParameters ) { // 互斥量在未創(chuàng)建之前是不可用的 xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { // 創(chuàng)建成功 // 在這里可以使用這個互斥量了 } }
4創(chuàng)建遞歸互斥量
4.1函數(shù)描述
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
用于創(chuàng)建遞歸互斥量。被創(chuàng)建的互斥量可以被API函數(shù)xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()使用,但不可以被API函數(shù)xSemaphoreTake()和xSemaphoreGive()使用。
遞歸類型的互斥量可以被擁有者重復(fù)獲取。擁有互斥量的任務(wù)必須調(diào)用API函數(shù)xSemaphoreGiveRecursive()將擁有的遞歸互斥量全部釋放后,該信號量才真正被釋放。比如,一個任務(wù)成功獲取同一個互斥量5次,那么這個任務(wù)要將這個互斥量釋放5次之后,其它任務(wù)才能獲取到它。
遞歸互斥量具有優(yōu)先級繼承機制,因此任務(wù)獲得一次信號后必須在使用完后做一個釋放操作。
互斥量類型信號不可以用在中斷服務(wù)例程中。
4.2返回值
NULL表示互斥量創(chuàng)建失敗,否則返回互斥量句柄。
4.3用法舉例
xSemaphoreHandle xMutex; void vATask( void * pvParameters ) { // 互斥量未創(chuàng)建前是不能被使用的 xMutex = xSemaphoreCreateRecursiveMutex(); if( xMutex != NULL ) { // 創(chuàng)建成功 // 在這里創(chuàng)建互斥量 } }
5刪除信號量
5.1函數(shù)描述
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
刪除信號量。如果有任務(wù)阻塞在這個信號量上,則這個信號量不要刪除。
5.2參數(shù)描述
xSemaphore:信號量句柄
6獲取信號量
6.1函數(shù)描述
xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)
獲取信號量。信號量必須是通過API函數(shù)xSemaphoreCreateBinary()、xSemaphoreCreateCounting()和xSemaphoreCreateMutex()預(yù)先創(chuàng)建過的。注意,遞歸互斥量類型信號量不能使用該函數(shù)、不用在中斷服務(wù)程序中使用該函數(shù)。
6.2參數(shù)描述
xSemaphore
:信號量句柄
xTickToWait
:信號量無效時,任務(wù)最多等待的時間,單位是系統(tǒng)節(jié)拍周期個數(shù)。使用宏portTICK_PERIOD_MS可以輔助將系統(tǒng)節(jié)拍個數(shù)轉(zhuǎn)化為實際時間(以毫秒為單位)。如果設(shè)置為0,表示不是設(shè)置等待時間。如果INCLUDE_vTaskSuspend設(shè)置為1,并且參數(shù)xTickToWait為portMAX_DELAY則可以無限等待。
6.3返回值
成功獲取到信號量返回pdTRUE,否則返回pdFALSE。
6.4用法舉例
SemaphoreHandle_t xSemaphore = NULL; /*這個任務(wù)創(chuàng)建信號量 */ void vATask( void * pvParameters ) { /*創(chuàng)建互斥型信號量,用于保護共享資源。*/ xSemaphore = xSemaphoreCreateMutex(); } /* 這個任務(wù)使用信號量 */ void vAnotherTask( void * pvParameters ) { /* ... 做其它事情. */ if( xSemaphore != NULL ) { /*如果信號量無效,則最多等待10個系統(tǒng)節(jié)拍周期。*/ if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) { /*到這里我們獲取到信號量,現(xiàn)在可以訪問共享資源了*/ /* ... */ /* 完成訪問共享資源后,必須釋放信號量*/ xSemaphoreGive( xSemaphore ); } else { /* 沒有獲取到信號量,這里處理異常情況。*/ } } }
7獲取信號量(帶中斷保護)
7.1函數(shù)描述
xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
signedBaseType_t *pxHigherPriorityTaskWoken)
API函數(shù)xSemaphoreTake()的另一版本,用于中斷服務(wù)程序。
7.2參數(shù)描述
xSemaphore
:信號量句柄
pxHigherPriorityTaskWoken
:如果*pxHigherPriorityTaskWoken為pdTRUE,則需要在中斷退出前手動進行一次上下文切換。從FreeRTOS V7.3.0開始,該參數(shù)為可選參數(shù),并可以設(shè)置為NULL。
7.3返回值
信號量成功獲取返回pdTRUE,否則返回pdFALSE。
8獲取遞歸互斥量
8.1函數(shù)描述
xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );
獲取遞歸互斥信號量?;コ饬勘仨毷峭ㄟ^API函數(shù)xSemaphoreCreateRecursiveMutex()創(chuàng)建的類型。
文件FreeRTOSConfig.h中的宏configUSE_RECURSIVE_MUTEXES必須設(shè)置成1,此函數(shù)才有效。
已經(jīng)獲取遞歸互斥量的任務(wù)可以重復(fù)獲取該遞歸互斥量。
使用xSemaphoreTakeRecursive()函數(shù)成功獲取幾次遞歸互斥量,就要使用xSemaphoreGiveRecursive()函數(shù)返還幾次,在此之前遞歸互斥量都處于無效狀態(tài)。比如,某個任務(wù)成功獲取5次遞歸互斥量,那么在它沒有返還5次該遞歸互斥量之前,這個互斥量對別的任務(wù)無效。
8.2參數(shù)描述
xMutex
:互斥量句柄,必須是使用API函數(shù)xSemaphoreCreateRecursiveMutex()返回的。
xTickToWait
:互斥量無效時,任務(wù)最多等待的時間,單位是系統(tǒng)節(jié)拍周期個數(shù)。使用宏portTICK_PERIOD_MS可以輔助將系統(tǒng)節(jié)拍個數(shù)轉(zhuǎn)化為實際時間(以毫秒為單位)。如果設(shè)置為0,表示不是設(shè)置等待時間。如果任務(wù)已經(jīng)擁有信號量則xSemaphoreTakeRecursive()立即返回,不管xTickToWait是什么值。
8.3返回值
成功獲取遞歸互斥量返回pdTURE,否則返回pdFALSE。
8.4用法舉例
SemaphoreHandle_t xMutex = NULL; // 這個任務(wù)創(chuàng)建互斥量 void vATask( void *pvParameters ) { // 這個互斥量用于保護共享資源 xMutex =xSemaphoreCreateRecursiveMutex(); } //這個任務(wù)使用互斥量 void vAnotherTask( void *pvParameters ) { // ... 做其它事情. if( xMutex != NULL ) { // 如果互斥量無效,則最多等待10系統(tǒng)時鐘節(jié)拍周期. if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) { // 到這里我們成功獲取互斥量并可以訪問共享資源了 // ... // 由于某種原因,某些代碼需要在一個任務(wù)中多次調(diào)用API函數(shù) // xSemaphoreTakeRecursive()。當(dāng)然不會像本例中這樣連續(xù)式 //調(diào)用,實際代碼會有更加復(fù)雜的結(jié)構(gòu) xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // 我們獲取一個互斥量三次,所以我們要將這個互斥量釋放三次 //它才會變得有效。再一次說明,實際代碼可能會更加復(fù)雜。 xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); // 到這里,這個共享資源可以被其它任務(wù)使用了. } else { // 處理異常情況 } } }
9釋放信號量
9.1函數(shù)描述
xSemaphoreGive(SemaphoreHandle_t xSemaphore )
用于釋放一個信號量。信號量必須是API函數(shù)xSemaphoreCreateBinary()、xSemaphoreCreateCounting()或xSemaphoreCreateMutex() 創(chuàng)建的。必須使用API函數(shù)xSemaphoreTake()獲取這個信號量。
這個函數(shù)絕不可以在中斷服務(wù)例程中使用,可以使用帶中斷保護版本的API函數(shù)xSemaphoreGiveFromISR()來實現(xiàn)相同功能。
這個函數(shù)不能用于使用API函數(shù)xSemaphoreCreateRecursiveMutex()所創(chuàng)建的遞歸互斥量。
9.2參數(shù)描述
xSemaphore:信號量句柄。
9.3返回值
信號量釋放成功返回pdTRUE,否則返回pdFALSE。
9.4用法舉例
SemaphoreHandle_t xSemaphore = NULL; voidvATask( void * pvParameters ) { // 創(chuàng)建一個互斥量,用來保護共享資源 xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { //我們希望這個函數(shù)調(diào)用失敗,因為首先要獲取互斥量 } // 獲取信號量,不等待 if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) { // 現(xiàn)在我們擁有互斥量,可以安全的訪問共享資源 if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { //我們不希望這個函數(shù)調(diào)用失敗,因為我們必須 //要釋放已獲取的互斥量 } } } }
10釋放信號量(帶中斷保護)
10.1函數(shù)描述
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
signed BaseType_t *pxHigherPriorityTaskWoken )
釋放信號量。是API函數(shù)xSemaphoreGive()的另個版本,用于中斷服務(wù)程序。信號量必須是通過API函數(shù)xSemaphoreCreateBinary()或xSemaphoreCreateCounting()創(chuàng)建的。這里沒有互斥量,是因為互斥量不可以用在中斷服務(wù)程序中。
10.2參數(shù)描述
xSemaphore
:信號量句柄
pxHigherPriorityTaskWoken
:如果*pxHigherPriorityTaskWoken為pdTRUE,則需要在中斷退出前人為的經(jīng)行一次上下文切換。從FreeRTOS V7.3.0開始,該參數(shù)為可選參數(shù),并可以設(shè)置為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 ) { /* 我們使用信號量同步,所以先創(chuàng)建一個二進制信號量.必須確保 在創(chuàng)建這個二進制信號量之前,中斷不會訪問它。*/ xSemaphore = xSemaphoreCreateBinary(); for( ;; ) { /* 我們希望每產(chǎn)生10次定時器中斷,任務(wù)運行一次。*/ if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) { /* 到這里成功獲取到信號量*/ ... /* 我們成功執(zhí)行完一次,由于這是個死循環(huán),所以任務(wù)仍會 阻塞在等待信號量上。信號量由ISR釋放。*/ } } } /* 定時器 ISR */ void vTimerISR( void * pvParameters ) { static unsigned char ucLocalTickCount = 0; static signed BaseType_txHigherPriorityTaskWoken; /*定時器中斷發(fā)生 */ ...執(zhí)行其它代碼 /*需要vATask() 運行嗎? */ xHigherPriorityTaskWoken = pdFALSE; ucLocalTickCount++; if( ucLocalTickCount >= TICKS_TO_WAIT ) { /* 釋放信號量,解除vATask任務(wù)阻塞狀態(tài) */ xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); /* 復(fù)位計數(shù)器 */ ucLocalTickCount = 0; } /* 如果 xHigherPriorityTaskWoken 表達(dá)式為真,需要執(zhí)行一次上下文切換*/ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
11釋放遞歸互斥量
11.1函數(shù)描述
xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )
釋放一個遞歸互斥量。互斥量必須是使用 API函數(shù)xSemaphoreCreateRecursiveMutex()創(chuàng)建的。文件FreeRTOSConfig.h中宏configUSE_RECURSIVE_MUTEXES必須設(shè)置成1本函數(shù)才有效。
11.2參數(shù)描述
xMutex:互斥量句柄。必須是函數(shù)xSemaphoreCreateRecursiveMutex()返回的值。
11.3返回值
如果遞歸互斥量釋放成功,返回pdTRUE。
11.4用法舉例
見“8 獲取遞歸互斥量”。
12獲取互斥量持有任務(wù)的句柄
12.1函數(shù)描述
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
返回互斥量持有任務(wù)的句柄(如果有的話),互斥量由參數(shù)xMutex指定。
如果調(diào)用此函數(shù)的任務(wù)持有互斥量,那么可以可靠的返回任務(wù)句柄,但是如果是別的任務(wù)持有互斥量,則不總可靠。
文件FreeRTOSConfig.h中宏configUSE_MUTEXES必須設(shè)置成1本函數(shù)才有效。
12.2參數(shù)描述
xMutex:互斥量句柄
12.3返回值
返回互斥量持有任務(wù)的句柄。如果參數(shù)xMutex不是互斥類型信號量或者雖然互斥量有效但這個互斥量不被任何任務(wù)持有則返回NULL。
這是FreeRTOS基礎(chǔ)篇的最后一篇博文,到這里我們已經(jīng)可以移植、熟練使用FreeRTOS了。但如果想知道FreeRTOS背后的運行機制,這些是遠(yuǎn)遠(yuǎn)不夠的,下面要走的路還會很長。要不要了解FreeRTOS背后運行機制,全憑各位的興趣,畢竟我們即使不清楚汽車的構(gòu)造細(xì)節(jié),但只要掌握駕駛技巧也可以很好的開車的。使用RTOS也與之相似,只要我們掌握了基礎(chǔ)篇的那些知識,我們已經(jīng)可以很好的使用FreeRTOS了。探索FreeRTOS背后運行的機制,可以讓我們更優(yōu)雅、更少犯錯、更舉重若輕的的使用RTOS。
FreeRTOS高級篇已經(jīng)開始寫了,更多關(guān)于FreeRTOS信號量API函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
FreeRTOS實時操作系統(tǒng)的任務(wù)概要講解
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)的任務(wù)概要講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS進階系統(tǒng)節(jié)拍時鐘示例的完全解析
這篇文章主要為大家介紹了FreeRTOS進階系統(tǒng)節(jié)拍時鐘示例的完全解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)的任務(wù)應(yīng)用函數(shù)詳解
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)的任務(wù)應(yīng)用函數(shù)的解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)的列表與列表項操作示例
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)的列表與列表項操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04FreeRTOS實時操作系統(tǒng)的內(nèi)核控制示例解析
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)的內(nèi)核控制示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04