ThreadX——IPC應用之信號量

一、應用簡介

在RTOS的應用開發中,信號量也是經常使用到的一種用於多任務之間信息同步、資源互斥訪問的一種手段,常用於協調多個任務訪問同一資源的場景。信號量又分為計數信號量互斥信號量。計數信號量可以表示整體資源數量,當獲取一個資源後計數信號量減一,釋放一個資源後計數信號量加一,當信號量為0時即表明資源被全部分配導致無法再獲取資源,任務可以掛起等待直到有資源可用。互斥信號量是可以理解為只能維護資源數量為1的二值計數信號量(值為0或1),但是互斥信號量又不同於計數信號量,因為它還具有優先級繼承的機制(優先級繼承機制是RTOS中為了避免出現優先級翻轉問題而做的處理方式。簡單來說就是如果低優先級持有互斥信號量那麼高優先級任務想訪問互斥量就會失敗而掛起等待互斥量被釋放,此時反而是低優先級任務在運行,這就出現了優先級翻轉。為了避免該情況RTOS處理方式是把正在持有互斥量運行的低優先級任務的優先級提高到與等待訪問互斥資源的高優先級任務同等優先級,這就是優先級繼承。等互斥量被釋放後RTOS會將該任務恢復到之前的低優先級)。

信號量應用有一個很貼切的舉例就是停車場管理。可以把停車場的所有停車位看作計數信號量初始值,當有車輛進入停車場計數信號量值減一,當有車輛離開計數信號量值加一。當值為0時說明沒有空車位了,外面車輛無法再停進來,需要等到有空車位出現(計數信號量非0)。停車場中的每一個停車位又可以用互斥信號量來表示,當有車輛佔據某車位時表明該車輛持有該車位的互斥信號量,其他車輛無法再停在該車位(如果有人非要杠說萬一車位大可以停2個小車那我莫法哈哈哈)。當該車位車輛離開後表明釋放該車位的互斥信號量,此時其他車輛可以停入該車位。

二、API簡介

下面列出ThreadX中使用信號量常用到的函數

1、創建計數信號量

  • 描述

    此服務為線程間同步創建一個計數信號量。初始信號量計數被指定為輸入參數

  • 參數

    • semaphore_ptr 指向信號量控制塊的指針
    • name_ptr 指向信號量名稱的指針
    • initial_count 指定此信號量的初始計數。 合法值的範圍是0x00000000至0xFFFFFFFF
  • 返回值

    • TX_SUCCESS (0x00) 成功創建信號量
    • TX_SEMAPHORE_ERROR (0x0C) 無效的信號量指針。指針是 NULL 或者已經創建了信號量
    • TX_CALLER_ERROR (0x13) 該服務的調用者無效
UINT tx_semaphore_create(
    TX_SEMAPHORE *semaphore_ptr,
    CHAR *name_ptr, 
    ULONG initial_count);

2、刪除計數信號量

  • 描述

    • 此服務刪除指定的計數信號量,恢復所有掛起的等待信號量的線程,並返回TX_DELETED狀態。

    • 在刪除信號量之前,應用程序必須確保完成(或禁用)此信號量的 put_notify 回調。此外,應用程序必須防止再使用已刪除的信號量。

  • 參數

    • semaphore_ptr 指向先前創建的信號量的指針
  • 返回值

    • TX_SUCCESS (0x00) 成功刪除計數信號量
    • TX_SEMAPHORE_ERROR (0x0C) 無效的計數信號量指針
    • TX_CALLER_ERROR (0x13) 該服務的調用者無效

UINT tx_semaphore_delete(TX_SEMAPHORE *semaphore_ptr);

3、獲取計數信號量

  • 描述

    此服務從指定的計數信號量獲取一個信號量,指定的信號量計數減少一個。

  • 參數

    • semaphore_ptr 指向先前創建的計數信號量的指針
    • wait_optionTX_NO_WAIT (0x00000000)不等待直接返回獲取結果;TX_WAIT_FOREVER (0xFFFFFFFF)一直掛起等待直到獲取到信號量;0x00000001 ~ 0xFFFFFFFE指定掛起等待多少個心跳節拍。
  • 返回值

    • TX_SUCCESS (0x00) 成功獲取信號量
    • TX_DELETED (0x01) 線程掛起時刪除了計數信號量
    • TX_NO_INSTANCE (0x0D) 服務無法檢索計數信號量的實例(在指定的等待時間內信號量計數為零)
    • TX_WAIT_ABORTED (0x1A) 被其他線程或計時器或中斷打斷而導致服務掛起
    • TX_SEMAPHORE_ERROR (0x0C) 計數信號量指針無效
    • TX_WAIT_ERROR (0x04) 在非線程調用中指定了TX_NO_WAIT以外的等待選項
UINT tx_semaphore_get(
    TX_SEMAPHORE *semaphore_ptr,
    ULONG wait_option);

4、獲取計數信號量信息

  • 描述

    該服務檢索有關指定信號量的信息

  • 參數

    參數為TX_NULL表示不獲取該參數的信息

    • semaphore_ptr 指向信號量控制塊的指針
    • name 指向信號量名稱的指針的目標指針
    • current_value 指向當前信號量計數的目標的指針
    • first_suspended 指向這個信號量掛起列表中第一個線程的指針
    • suspended_count 指向當前掛起在此信號量上的線程數的指針
    • next_semaphore 指向下一個創建的信號量指針的目標指針
  • 返回值

    • TX_SUCCESS (0x00) 獲取信息成功
    • TX_SEMAPHORE_ERROR (0x0C) 無效的信號量指針
UINT tx_semaphore_info_get(
    TX_SEMAPHORE *semaphore_ptr,
    CHAR **name, ULONG *current_value,
    TX_THREAD **first_suspended,
    ULONG *suspended_count,
    TX_SEMAPHORE **next_semaphore);

5、增加計數信號量

  • 描述
    • 該服務將指定的信號量計數加一。
    • 如果在信號量為0xFFFFFFFF時調用此服務,則新的put操作將導致信號量重置為零。
  • 參數
    • semaphore_ptr 指向創建的計數信號量控制塊的指針
  • 返回值
    • TX_SUCCESS (0x00) 成功放置信號量
    • TX_SEMAPHORE_ERROR (0x0C) 指向信號量的指針無效

UINT tx_semaphore_put(TX_SEMAPHORE *semaphore_ptr);

6、增加指定上限的計數信號量

  • 描述

    該服務將指定的計數信號量增加一個。 如果計數信號量的當前值大於或等於指定的上限,則不會增加信號量,並且將返回TX_CEILING_EXCEEDED錯誤。

  • 參數

    • semaphore_ptr 指向先前創建的信號量的指針
    • ceiling 信號量所允許的上限值(有效值範圍是1到0xFFFFFFFF)
  • 返回值

    • TX_SUCCESS (0x00) 操作成功
    • TX_CEILING_EXCEEDED (0x21) Put請求超過上限
    • TX_INVALID_CEILING (0x22) 為上限提供了無效值零
    • TX_SEMAPHORE_ERROR (0x0C) 無效的信號量指針
UINT tx_semaphore_ceiling_put(
    TX_SEMAPHORE *semaphore_ptr,
    ULONG ceiling);

7、創建互斥信號量

  • 描述

    此服務為線程間互斥創建互斥體以保護資源。

  • 參數

    • mutex_ptr 指向互斥量控制塊的指針
    • name_ptr 指向互斥量名稱的指針
    • priority_inherit 指定此互斥對象是否支持優先級繼承。 如果此值為TX_INHERIT,則支持優先級繼承。 如果指定TX_NO_INHERIT,則此互斥鎖不支持優先級繼承。
  • 返回值

    • TX_SUCCESS (0x00) 成功創建信號量
    • TX_MUTEX_ERROR (0x1C)無效的互斥指針。 指針為NULL或互斥體已創建
    • TX_CALLER_ERROR (0x13) 該服務的調用者無效
    • TX_INHERIT_ERROR (0x1F) 無效的優先級繼承參數
UINT tx_mutex_create(
    TX_MUTEX *mutex_ptr,
    CHAR *name_ptr, 
    UINT priority_inherit); 

8、刪除互斥信號量

  • 描述

    • 該服務將刪除指定的互斥信號量。 恢復所有等待互斥的已暫停線程,並返回TX_DELETED返回狀態。

    • 應用程序應防止使用已刪除的互斥信號量

  • 參數

    • mutex_ptr 指向先前創建的互斥體的指針
  • 返回值

    • TX_SUCCESS (0x00) 操作成功
    • TX_MUTEX_ERROR (0x1C) 無效的互斥體指針
    • TX_CALLER_ERROR (0x13) 該服務的調用者無效

UINT tx_mutex_delete(TX_MUTEX *mutex_ptr);

9、獲取互斥信號量

  • 描述
    • 該服務嘗試獲取指定互斥鎖的獨佔所有權。 如果調用線程已經擁有互斥鎖,則內部計數器將遞增,並返回成功狀態
    • 如果互斥鎖由另一個線程擁有,並且該線程具有更高的優先級,並且在互斥鎖創建時指定了優先級繼承,則優先級較低的線程的優先級將暫時提高到調用線程的優先級。
    • 擁有互斥量的低優先級線程的優先級在互斥體所有權期間絕對不能由外部線程修改
  • 參數
    • mutex_ptr 指向先前創建的互斥體的指針
    • wait_optionTX_NO_WAIT (0x00000000)不等待直接返回獲取結果;TX_WAIT_FOREVER (0xFFFFFFFF)一直掛起等待直到獲取到信號量;0x00000001 ~ 0xFFFFFFFE指定掛起等待多少個心跳節拍。
  • 返回值
    • TX_SUCCESS (0x00) 操作成功
    • TX_DELETED (0x01) 線程暫停時互斥體被刪除
    • TX_NOT_AVAILABLE (0x1D) 服務無法在指定的等待時間內獲得互斥鎖的所有權
    • TX_WAIT_ABORTED (0x1A) 被其他線程或計時器或中斷打斷而導致服務掛起
    • TX_MUTEX_ERROR (0x1C) 無效的互斥體指針
    • TX_WAIT_ERROR (0x04) 在非線程調用中指定了TX_NO_WAIT以外的等待選項
    • TX_CALLER_ERROR (0x13) 該服務的調用者無效
UINT tx_mutex_get(
    TX_MUTEX *mutex_ptr, 
    ULONG wait_option);

10、釋放互斥信號量

  • 描述
    • 此服務將釋放互斥信號量
    • 如果在創建互斥對象時選擇了優先級繼承,那麼釋放線程的優先級將恢復到它最初獲得互斥對象所有權時的優先級。在擁有互斥對象期間對釋放線程所做的任何其他優先級更改都可能被撤消。
  • 參數
    • mutex_ptr 指向先前創建的互斥體的指針
  • 返回值
    • TX_SUCCESS (0x00) 操作成功
    • TX_NOT_OWNED (0x1E) 互斥對象不歸調用者所有
    • TX_MUTEX_ERROR (0x1C) 無效的互斥體指針
    • TX_CALLER_ERROR (0x13) 該服務的調用者無效

UINT tx_mutex_put(TX_MUTEX *mutex_ptr);

三、實例演示

  • 該實例用到的硬件資源:一個串口、兩個按鍵、一個LED
  • 創建一個計數信號量,指定數量上限為3,KEY1申請計數信號量,KEY2增加計數信號量
  • 創建一個互斥信號量,用於KEY1、KEY2互斥,即兩個按鍵不能同時按下
  • 創建2個任務,一個用於KEY1任務,一個用於KEY2任務,
#define DEMO_STACK_SIZE         (2 * 1024)
#define DEMO_BYTE_POOL_SIZE     (32 * 1024)

TX_THREAD       thread_0;
TX_THREAD       thread_1;

TX_BYTE_POOL	byte_pool_0;
UCHAR			memory_area[DEMO_BYTE_POOL_SIZE];

TX_MUTEX 		tx_semaphore_mutex;	// 互斥信號量
TX_SEMAPHORE 	tx_semaphore_count; // 計數信號量
void tx_application_define(void *first_unused_memory)
{
	UINT 	status;
	CHAR    *pointer = TX_NULL;
	
	/* 創建互斥信號量 */
	status = tx_mutex_create(&tx_semaphore_mutex,"tx_semaphore_mutex",TX_NO_INHERIT);
	if (TX_SUCCESS != status)
	{
		// 創建失敗處理
	}
	/* 創建計數信號量 */
	status = tx_semaphore_create(&tx_semaphore_count, "tx_semaphore_count", 3);
	if (TX_SUCCESS != status)
	{
		// 創建失敗處理
	}

	 /* Create a byte memory pool from which to allocate the thread stacks.  */
    tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE);

	/* Allocate the stack for thread 0.  */
    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
    /* Create the main thread.  */
    tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,  
            pointer, DEMO_STACK_SIZE, 
            1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);

	/* Allocate the stack for thread 1.  */
    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
    /* Create threads 1  */
    tx_thread_create(&thread_1, "thread 1", thread_1_entry, 0,  
            pointer, DEMO_STACK_SIZE, 
            2, 2, TX_NO_TIME_SLICE, TX_AUTO_START);
}
void    thread_0_entry(ULONG thread_input)
{
	UINT status, key_flag = 0;
	
	CHAR 			*name;
	ULONG 			current_value;
	TX_THREAD 		*first_suspended;
	ULONG 			suspended_count;
	TX_SEMAPHORE 	*next_semaphore;
	
    while(1)
    {
		if (0 == key_flag)
		{
			if (GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin))
			{
				key_flag = 1;
                /*按鍵按下,獲取互斥信號量*/
				status = tx_mutex_get(&tx_semaphore_mutex, TX_NO_WAIT);
				if (TX_SUCCESS == status)
				{
                    /*獲取計數信號量*/
					if (TX_SUCCESS == tx_semaphore_get(&tx_semaphore_count, TX_NO_WAIT))
					{
						HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
                        /*獲取計數信號量信息*/
						tx_semaphore_info_get(&tx_semaphore_count, 
											  &name, 
											  &current_value, 
											  &first_suspended, 
											  &suspended_count, 
											  &next_semaphore);
						printf("key1 pressed, current count semaphore is %d\r\n",(int)current_value);
					}
					else
					{
						printf("key1 failed to get count semaphore\r\n");
					}
				}
				else
				{
					printf("key1 failed to get mutex semaphore\r\n");
				}
			}
		}
		else
		{
			if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin))
			{
				key_flag = 0;
                 /*按鍵鬆開,釋放互斥信號量*/
				tx_mutex_put(&tx_semaphore_mutex);
			}
		}
		
		tx_thread_sleep(20);
    }
}
void    thread_1_entry(ULONG thread_input)
{
	UINT status, key_flag = 0;
	ULONG current_value;

    while(1)
    {
		if (0 == key_flag)
		{
			if (GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin))
			{
				key_flag = 1;
                 /*按鍵按下,獲取互斥信號量*/
				status = tx_mutex_get(&tx_semaphore_mutex, TX_NO_WAIT);
				if (TX_SUCCESS == status)
				{
                    /*計數信號量加一,指定上限為3*/
					//tx_semaphore_put(&tx_semaphore_count);
					tx_semaphore_ceiling_put(&tx_semaphore_count,3);
					 /*獲取計數信號量信息*/
					tx_semaphore_info_get(&tx_semaphore_count, 
                                          TX_NULL, 
                                          &current_value, 
                                          TX_NULL, 
                                          TX_NULL, 
                                          TX_NULL);
					printf("key2 pressed, current count semaphore is %d\r\n",(int)current_value);
				}
				else
				{
					printf("key2 failed to get mutex semaphore\r\n");
				}
			}
		}
		else
		{
			if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin))
			{
				key_flag = 0;
                /*按鍵鬆開,釋放互斥信號量*/
				tx_mutex_put(&tx_semaphore_mutex);
			}
		}
		
		tx_thread_sleep(20);
    }
}

串口打印演示結果如下


[20:51:33.330]收←◆key1 pressed, current count semaphore is 2

[20:51:34.034]收←◆key1 pressed, current count semaphore is 1

[20:51:34.556]收←◆key1 pressed, current count semaphore is 0

[20:51:34.939]收←◆key1 failed to get count semaphore

[20:51:35.522]收←◆key1 failed to get count semaphore

[20:51:37.004]收←◆key2 pressed, current count semaphore is 1

[20:51:37.488]收←◆key2 pressed, current count semaphore is 2

[20:51:37.851]收←◆key2 pressed, current count semaphore is 3

[20:51:38.234]收←◆key2 pressed, current count semaphore is 3

[20:51:38.556]收←◆key2 pressed, current count semaphore is 3

[20:51:44.925]收←◆key1 pressed, current count semaphore is 2

[20:51:46.579]收←◆key2 failed to get mutex semaphore

[20:51:52.102]收←◆key2 pressed, current count semaphore is 3

[20:51:52.987]收←◆key1 failed to get mutex semaphore