11.1 信號量說明
信號量是操作系統(tǒng)中重要的一部分,信號量一般用來進行資源管理和任務(wù)同步, FreeRTOS中信號量又分為二值信號量、 計數(shù)型信號量、互斥信號量和遞歸互斥信號量。信號量在實際應(yīng)用中最廣泛的兩個用途是:
- 臨界資源的鎖機制:用于控制共享資源訪問的場景相當于一個上鎖機制, 代碼只有獲得了這個鎖的鑰匙才能夠執(zhí)行。
- 多個任務(wù)同步機制:用于任務(wù)與任務(wù)或中斷與任務(wù)之間的同步。
在編寫中斷服務(wù)函數(shù)的時候我們都知道一定要快進快出,中斷服務(wù)函數(shù)里面不能放太多的代碼,否則的話會影響的中斷的實時性。 裸機編寫中斷服務(wù)函數(shù)的時候一般都只是在中斷服務(wù)函數(shù)中打個標記,然后在其他的地方根據(jù)標記來做具體的處理過程。在使用 RTOS 系統(tǒng)的時候我們就可以借助信號量完成此功能, 當中斷發(fā)生的時候就釋放信號量,中斷服務(wù)函數(shù)不做具體的處理。具體的處理過程做成一個任務(wù),這個任務(wù)會獲取信號量,如果獲取到信號量就說明中斷發(fā)生了,那么就開始完成相應(yīng)的處理,這樣做的好處就是中斷執(zhí)行時間非常短。 這個例子就是中斷與任務(wù)之間使用信號量來完成同步,當然了, 任務(wù)與任務(wù)之間也可以使用信號量來完成同步。
在實際4G/WiFi等網(wǎng)絡(luò)應(yīng)用中,一般最簡單的方法就是使用一個任務(wù)去查詢 4G/WiFi 模塊是否有數(shù)據(jù)到來,當有數(shù)據(jù)的時候就處理這個網(wǎng)絡(luò)數(shù)據(jù)。但這樣使用輪詢的方式是很浪費CPU 資源的,而且也阻止了其他任務(wù)的運行。一種理想的解決方法應(yīng)該是當沒有網(wǎng)絡(luò)數(shù)據(jù)的時候網(wǎng)絡(luò)任務(wù)就進入阻塞態(tài),把 CPU 讓給其他的任務(wù),當有數(shù)據(jù)的時候網(wǎng)絡(luò)任務(wù)才去執(zhí)行。現(xiàn)在使用二值信號量就可以實現(xiàn)這樣的功能,任務(wù)通過獲取信號量來判斷是否有網(wǎng)絡(luò)數(shù)據(jù),沒有的話就進入阻塞態(tài),而網(wǎng)絡(luò)中斷服務(wù)函數(shù)通過釋放信號量來通知任務(wù)以太網(wǎng)外設(shè)接收到了網(wǎng)絡(luò)數(shù)據(jù),網(wǎng)絡(luò)任務(wù)可以去提取處理了。網(wǎng)絡(luò)任務(wù)只是在一直的獲取二值信號量,它不會釋放信號量,而中斷服務(wù)函數(shù)是一直在釋放信號量,它不會獲取信號量。
接下來,我們以二值信號量為例,講解信號量實現(xiàn)任務(wù)同步的大致流程。其實,二值信號量其實就是一個只有一個隊列項的隊列,這個特殊的隊列要么是滿的,要么是空的。二值信號量通常用于互斥訪問或任務(wù)同步,它與互斥信號量非常類似,但是還是有一些細微的差別,互斥信號量擁有優(yōu)先級繼承機制,二值信號量沒有優(yōu)先級繼承。因此二值信號另更適合用于同步(任務(wù)與任務(wù)或任務(wù)與中斷的同步),而互斥信號量適合用于簡單的互斥訪問。
- 任務(wù)獲取信號量有效
如下圖所示,任務(wù)Task通過調(diào)用 xSemaphoreTake() 函數(shù)獲取信號量,但此時二值信號量無效,所以任務(wù)Task進入到阻塞態(tài)。 - 中斷釋放信號量
當有數(shù)據(jù)到來產(chǎn)生中斷后,在中斷服務(wù)處理程序中通過調(diào)用 xSemaphoreGiveFromISR() 函數(shù)釋放信號量,此后二值信號量有效。 - 任務(wù)獲取信號量有效
由于此時信號量已經(jīng)有效了,所以任務(wù) Task 獲取信號量成功,任務(wù)從阻塞態(tài)解除,開始執(zhí)行相關(guān)的處理過程。 - 任務(wù)再次進入阻塞
由于任務(wù)一般是一個大的死循環(huán),所以在任務(wù)做完相關(guān)處理以后就會再次調(diào)用 xSemaphoreTake() 函數(shù)獲取信號量,但此時信號量已經(jīng)失效,因此任務(wù)將進入到狀態(tài)1下阻塞等待信號量有效。
11.2 信號量API說明
11.2.1 獲取信號量
不管是二值信號量、計數(shù)型信號量還是互斥信號量,它們都使用以下兩個函數(shù)獲取信號量。
/*
參數(shù):
xSemaphore: 要獲取的信號量句柄。
?? xBlockTime: 阻塞時間。??
返回值:
pdTRUE: 獲取信號量成功。
pdFALSE: 超時,獲取信號量失敗。
*/
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
/*
說明: 此函數(shù)用于在中斷服務(wù)函數(shù)中獲取信號量, 此函數(shù)用于獲取二值信號量和計數(shù)型信號量,絕對不能使用此函數(shù)來獲取互斥信號量。
參數(shù): xSemaphore: 要獲取的信號量句柄。
pxHigherPriorityTaskWoken: 標記退出此函數(shù)以后是否進行任務(wù)切換,這個變量的值由這三個函數(shù)來設(shè)置的,用戶不用進行設(shè)置,
用戶只需要提供一個變量來保存這個值就行了。當此值為 pdTRUE 的時候在退出中斷服務(wù)函數(shù)之前一定要進行一次任務(wù)切換。
回值: pdPASS: 獲取信號量成功。
pdFALSE: 獲取信號量失敗。
*/
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
11.2.2 釋放信號量
釋放信號量分為任務(wù)級和中斷級。不管是二值信號量、計數(shù)型信號量還是互斥信號量,它們都使用以下兩個函數(shù)釋放信號量。
/*
參數(shù):xSemaphore: 要釋放的信號量句柄。
返回值:
pdPASS: 釋放信號量成功;
errQUEUE_FULL: 釋放信號量失敗。
*/
BaseType_t xSemaphoreGive( xSemaphore )
/*
說明: 此函數(shù)用于在中斷中釋放信號量, 此函數(shù)只能用來釋放二值信號量和計數(shù)型信號量,絕對不能用來在中斷服務(wù)函數(shù)中釋放互斥信號量。
參數(shù): xSemaphore: 要釋放的信號量句柄。
pxHigherPriorityTaskWoken: 標記退出此函數(shù)以后是否進行任務(wù)切換,這個變量的值由這三個函數(shù)來設(shè)置的,用戶不用進行設(shè)置,
用戶只需要提供一個變量來保存這個值就行了。當此值為 pdTRUE 的時候在退出中斷服務(wù)函數(shù)之前一定要進行一次任務(wù)切換。
返回值:
pdPASS: 釋放信號量成功。
errQUEUE_FULL: 釋放信號量失敗。
*/
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t * pxHigherPriorityTaskWoken)
11.2.3 創(chuàng)建二值信號量
在FreeRTOS中,有兩個函數(shù)可以創(chuàng)建二值信號量:
/*
說明: 使用此函數(shù)創(chuàng)建二值信號量的話信號量所需要的 RAM 是由 FreeRTOS 的內(nèi)存管理部分來動態(tài)分配的。
返回值:
NULL: 二值信號量創(chuàng)建失敗。
其他值: 創(chuàng)建成功的二值信號量的句柄。
*/
SemaphoreHandle_t xSemaphoreCreateBinary( void )
/*
說明: 此函數(shù)也是創(chuàng)建二值信號量的,只不過使用此函數(shù)創(chuàng)建二值信號量的話信號量所需要的RAM需要由用戶來分配
參數(shù): pxSemaphoreBuffer: 此參數(shù)指向一個 StaticSemaphore_t 類型的變量,用來保存信號量結(jié)構(gòu)體。
返回值:
NULL: 二值信號量創(chuàng)建失敗。
其他值: 創(chuàng)建成功的二值信號量句柄。
*/
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer )
11.2.4 創(chuàng)建互斥信號量
在FreeRTOS中,有兩個函數(shù)可以創(chuàng)建互斥信號量:
/*
說明:此函數(shù)用于創(chuàng)建一個互斥信號量,所需要的內(nèi)存通過動態(tài)內(nèi)存管理方法分配。
參數(shù):無
返回值:
NULL: 互斥信號量創(chuàng)建失敗。
其他值: 創(chuàng)建成功的互斥信號量的句柄。
*/
SemaphoreHandle_t xSemaphoreCreateMutex( void )
/*
說明: 此函數(shù)也是創(chuàng)建互斥信號量的,只不過使用此函數(shù)創(chuàng)建互斥信號量的話信號量所需要的RAM 需要由用戶來分配
參數(shù): pxMutexBuffer: 此參數(shù)指向一個 StaticSemaphore_t 類型的變量,用來保存信號量結(jié)構(gòu)體。
返回值:
NULL: 互斥信號量創(chuàng)建失敗。
其他值: 創(chuàng)建成功的互斥信號量的句柄。
*/
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
11.2.5 創(chuàng)建計數(shù)信號量
在FreeRTOS中,有兩個函數(shù)可以創(chuàng)建計數(shù)信號量:
/*
說明: 此函數(shù)用于創(chuàng)建一個計數(shù)型信號量,所需要的內(nèi)存通過動態(tài)內(nèi)存管理方法分配。
參數(shù):
uxMaxCount: 計數(shù)信號量最大計數(shù)值,當信號量值等于此值的時候釋放信號量就會失敗。
uxInitialCount: 計數(shù)信號量初始值。
返回值:
NULL: 計數(shù)型信號量創(chuàng)建失敗。
其他值: 計數(shù)型信號量創(chuàng)建成功,返回計數(shù)型信號量句柄。
*/
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,UBaseType_t uxInitialCount )
/*
說明: 此函數(shù)也是用來創(chuàng)建計數(shù)型信號量的,使用此函數(shù)創(chuàng)建計數(shù)型信號量的時候所需要的內(nèi)存需要由用戶分配。
參數(shù):
uxMaxCount: 計數(shù)信號量最大計數(shù)值,當信號量值等于此值的時候釋放信號量就會失敗。
uxInitialCount: 計數(shù)信號量初始值。
pxSemaphoreBuffer: 指向一個 StaticSemaphore_t 類型的變量,用來保存信號量結(jié)構(gòu)體。
返回值:
NULL: 計數(shù)型信號量創(chuàng)建失敗。
其他值: 計數(shù)型號量創(chuàng)建成功,返回計數(shù)型信號量句柄。
*/
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount,StaticSemaphore_t * pxSemaphoreBuffer )
11.2.6 遞歸互斥信號量
遞歸互斥信號量可以看是一種特殊的互斥信號量,已經(jīng)獲取了信號量的任務(wù)就不能再次獲取這個互斥量,但是遞歸互斥信號量不同,已經(jīng)獲取了遞歸互斥信號量的任務(wù)可以再次獲取這個遞歸互斥信號量,次數(shù)不限。也就是在同一個任務(wù)中可以無限次獲取遞歸互斥信號量,中途不需要釋放,而互斥信號量獲取一次后就失效,需要再次釋放才有效。注意:任務(wù)獲取多少次遞歸互斥信號量,就要釋放多少次遞歸信號量。
/*
說明: 動態(tài)創(chuàng)建遞歸互斥信號量
參數(shù): 無
返回值:創(chuàng)建失敗NULL,創(chuàng)建成功返回信號量句柄。
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
/*
說明: 靜態(tài)創(chuàng)建遞歸互斥信號量
參數(shù): pxMutexBuffer:指向StaticSemaphore_t類型的變量,用來保存信號量結(jié)構(gòu)體。
返回值: 創(chuàng)建失敗NULL,創(chuàng)建成功返回信號量句柄。
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )
#define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore )
/*
說明: 釋放遞歸互斥信號量
參數(shù): xMutex: 信號量句柄
返回值:
pdPASS: 釋放信號量成功
pdFAIL: 釋放信號量失敗
*/
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )
/*
說明:獲取遞歸互斥信號量
參數(shù):
xMutex: 信號量句柄
xBlockTime:阻塞時間
返回值:
pdPASS: 獲取信號量成功
pdFAIL: 獲取信號量失敗
*/
xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex, TickType_t xBlockTime );
#define xSemaphoreTakeRecursive( xMutex, xBlockTime )
xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
11.3 信號量多任務(wù)同步實例
在這里我們將創(chuàng)建兩個Led線程,其中藍色Led線程每隔200ms閃爍一次,而紅色Led線程則每隔1200ms閃爍一次。然后我們使用二值信號量的機制,讓兩個Led線程同步交替閃爍一次。
11.3.1 創(chuàng)建兩個線程任務(wù)
如下圖所示,單擊窗格頂部的 “New Thread” 按鈕,添加兩個線程分別命名為 thread_led1 和 thread_led2 ,其它的保持默認配置即可,并重新生成代碼。
如下圖所示,單擊窗口的 “New Object” 按鈕,選擇 “Binary Semaphore” 添加一個二值信號量,然后修改該信號量的名稱為 g_led_semaphore ,并重新生成代碼。
11.3.2 修改信號量實例代碼
修改 thread_led1_entry.c 源碼如下:
#include "thread_led1.h"
/* Led Thread entry function */
/* pvParameters contains TaskHandle_t */
void thread_led1_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
R_BSP_PinAccessEnable(); /* Enable access to the PFS registers. */
/* TODO: add your own code here */
while (1)
{
xSemaphoreTake( g_led_semaphore, portMAX_DELAY );
R_BSP_PinWrite(LedRed, BSP_IO_LEVEL_HIGH);
vTaskDelay (600);
R_BSP_PinWrite(LedRed, BSP_IO_LEVEL_LOW);
vTaskDelay (600);
}
}
- 如果不加信號量操作部分代碼,紅色Led將每隔1200ms閃爍一次;
- 這里調(diào)用了xSemaphoreTake() 函數(shù)來獲取信號量 g_led_semaphore,如果 thread_led2 線程沒有運行并釋放信號量的話,它將會阻塞;
- xSemaphoreTake() 函數(shù)在獲取到信號之后,將會讓紅色Led閃爍一次;
- 之后該線程將會再次調(diào)用了xSemaphoreTake() 函數(shù),等待 thread_led2 線程釋放信號;
修改 thread_led2_entry.c 源碼如下:
#include "thread_led2.h"
/* Led Thread entry function */
/* pvParameters contains TaskHandle_t */
void thread_led2_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
R_BSP_PinAccessEnable(); /* Enable access to the PFS registers. */
/* TODO: add your own code here */
while (1)
{
if( pdTRUE == xSemaphoreGive( g_led_semaphore) )
{
R_BSP_PinWrite(LedBlue, BSP_IO_LEVEL_HIGH);
vTaskDelay (100);
R_BSP_PinWrite(LedBlue, BSP_IO_LEVEL_LOW);
vTaskDelay (100);
}
}
}
- 如果不加信號量操作部分代碼,藍色Led將每隔200ms閃爍一次;
- 這里調(diào)用了xSemaphoreGive() 函數(shù)釋放信號量 g_led_semaphore,用來同步通知 thread_led1 線程運行;
- xSemaphoreGive() 函數(shù)在釋放信號之后,如果 thread_led1 沒有來得及獲取信號量的話,則會返回pdFALSE;
- 這樣,如果 thread_led1 沒有運行的話,藍色Led也不會閃爍了,從而實現(xiàn)了兩個線程中的Led同步交替閃爍一次效果了;
-
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6827瀏覽量
123335 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
484瀏覽量
62182 -
信號量
+關(guān)注
關(guān)注
0文章
53瀏覽量
8344
發(fā)布評論請先 登錄
相關(guān)推薦
評論