UCOSIII信號量與互斥量

在 UCOSIII 中有可能會有多個任務會訪問共享資源,因此信號量最早用來控制任務存取共享資源,現在信號量也被用來實現任務間的同步以及任務和 ISR 間同步。在可剝奪的內核中,當任務獨佔式使用共享資源的時候,會出現低優先級的任務先於高優先級任務運行的現象,這個現象被稱爲優先級反轉,爲了解決優先級反轉這個問題,UCOSIII 引入了互斥信號量這個概念。

信號量

信號量像是一種上鎖機制,代碼必須獲得對應的鑰匙才能繼續執行,一旦獲得了鑰匙,也就意味着該任務具有進入被鎖部分代碼的權限。一旦執行至被鎖代碼段,則任務一直等待,直到對應被鎖部分代碼的鑰匙被再次釋放才能繼續執行。信號量分爲兩種:二進制信號量與計數型信號量,二進制信號量只能取 0 和 1 兩個值,計
數型信號量不止可以取 2 個值,在共享資源中只有任何可以使用信號量,中斷服務程序則不能使用

二進制信號量

某一資源對應的信號量爲 1 的時候,那麼就可以使用這一資源,如果對應資源的信號量爲0,那麼等待該信號量的任務就會被放進等待信號量的任務表中。在等待信號量的時候也可以設置超時,如果超過設定的時間任務沒有等到信號量的話那麼該任務就會進入就緒態。任務以“發信號”的方式操作信號量。可以看出如果一個信號量爲二進制信號量的話,一次只能一個任務使用共享資源。

計數型信號量

有時候我們需要可以同時有多個任務訪問共享資源,這個時候二進制信號量就不能使用了,計數型信號量就是用來解決這個問題的。比如某一個信號量初始化值爲 10,那麼只有前 10 個請求該信號量的任務可以使用共享資源,以後的任務需要等待前 10 個任務釋放掉信號量。每當有任務請求信號量的時候,信號量的值就會減 1,直到減爲 0。當有任務釋放掉信號量的時候信號量的值就會加 1。

創建信號量

OSSemCreate()

void OSSemCreate ( OS_SEM *p_sem,
				   CPU_CHAR *p_name,
				   OS_SEM_CTR cnt,
				   OS_ERR *p_err)

p_sem : 指向信號量控制塊,我們需要按照如下所示方式定義一個全局信號量,並將這個信號量的指針傳遞給函數 OSSemCreate()。
OS_SEM TestSem;
p_name: 指向信號量的名字。
cnt : 設置信號量的初始值,如果此值爲 1,代表此信號量爲二進制信號量,如果大於 1的話就代表此信號量爲計數型信號量。
p_err : 保存調用此函數後的返回的錯誤碼。

請求信號量

當一個任務需要獨佔式的訪問某個特定的系統資源時,需要與其他任務或中斷服務程序同步,或者需要等待某個事件的發生,應該調用函數
OSSemPend()

OS_SEM_CTR OSSemPend ( OS_SEM *p_sem,
					   OS_TICK timeout,
					   OS_OPT opt,
					   CPU_TS *p_ts,
					   OS_ERR *p_err)

p_sem : 指向一個信號量的指針。
timeout : 指定等待信號量的超時時間(時鐘節拍數),如果在指定時間內沒有等到信號量則允許任務恢復執行。如果指定時間爲 0 的話任務就會一直等待下去,直到等到信號量。
opt : 用於設置是否使用阻塞模式,有下面兩個選項。
OS_OPT_PEND_BLOCKING 指定信號量無效時,任務掛起以等待信號量。
OS_OPT_PEND_NON_BLOCKING 信號量無效時,任務直接返回。
p_ts : 指向一個時間戳,用來記錄接收到信號量的時刻,如果給這個參數賦值 NULL,則說明用戶沒有要求時間戳。
p_err : 保存調用本函數後返回的錯誤碼。

發送信號量

任務獲得信號量以後就可以訪問共享資源了,在任務訪問完共享資源以後必須釋放信號量,
釋放信號量也叫發送信號量,使用函數 OSSemPost()發送信號量。如果沒有任務在等待該信號
量的話則 OSSemPost()函數只是簡單的將信號量加 1,然後返回到調用該函數的任務中繼續運行。
如果有一個或者多個任務在等待這個信號量,則優先級最高的任務將獲得這個信號量,然後由
調度器來判定剛獲得信號量的任務是否爲系統中優先級最高的就緒任務,如果是,則系統將進
行任務切換,運行這個就緒任務,
OSSemPost()

OS_SEM_CTR OSSemPost ( OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)

p_sem : 指向一個信號量的指針
opt : 用來選擇信號量發送的方式。
OS_OPT_POST_1 僅向等待該信號量的優先級最高的任務發送信號量。
OS_OPT_POST_ALL 向等待該信號量的所有任務發送信號量。
OS_OPT_POST_NO_SCHED 該選項禁止在本函數內執行任務調度操作。即使
該函數使得更高優先級的任務結束掛起進入就緒狀態,也不會執行任務調度,而是會在其他後續函數中完成任務調度。
p_err : 用來保存調用此函數後返回的錯誤碼

信號量訪問共享資源區實驗

OS_SEM MY_SEM; //定義一個信號量,用於訪問共享資源
//創建一個信號量
OSSemCreate ((OS_SEM* )&MY_SEM, //指向信號量
			 (CPU_CHAR* )"MY_SEM", //信號量名字
			 (OS_SEM_CTR )1, //信號量值爲 1
			 (OS_ERR* )&err);
//任務 1 的任務函數
void task1_task(void *p_arg)
{
	OS_ERR err;
	u8 task1_str[]="First task Running!";
	while(1)
	{
		printf("\r\n 任務 1:\r\n");
		LCD_Fill(0,110,239,319,CYAN);
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); // 請求信號量(1)
		memcpy(share_resource,task1_str,sizeof(task1_str)); //向共享資源區拷貝數據
		delay_ms(200);
		printf("%s\r\n",share_resource); //串口輸出共享資源區數據
		OSSemPost (&MY_SEM,OS_OPT_POST_1,&err); // 發送信號量 (2)
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}
//任務 2 的任務函數
void task2_task(void *p_arg)
{
	OS_ERR err;
	u8 task2_str[]="Second task Running!";
	while(1)
	{
		printf("\r\n 任務 2:\r\n");
		LCD_Fill(0,110,239,319,BROWN);
		OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); // 請求信號量(3)
		memcpy(share_resource,task2_str,sizeof(task2_str)); //向共享資源區拷貝數據
		delay_ms(200);
		printf("%s\r\n",share_resource); //串口輸出共享資源區數據
		OSSemPost (&MY_SEM,OS_OPT_POST_1,&err); // 發送信號量 (4)
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}

任務同步實驗

信號量現在更多的被用來實現任務的同步以及任務和 ISR 間的同步,信號量用於任務同步
在這裏插入圖片描述
圖 10.4.1 中用一個小旗子代表信號量,小旗子旁邊的數值 N 爲信號量計數值, 表示發佈信號量的次數累積值,ISR 可以多次發佈信號量,發佈的次數會記錄爲 N。一般情況下,N 的初始值是 0,表示事件還沒有發生過。在初始化時,也可以將 N 的初值設爲大於零的某個值,來表示初始情況下有多少信號量可用。

等待信號量的任務旁邊的小沙漏表示等待任務可以設定超時時間。超時的意思是該任務只會等待一定時間的信號量,如果在這段時間內沒有等到信號量,UCOSIII 就會將任務置於就緒表中,並返回錯誤碼。

例 10-3 :創建 3 個任務,任務 A 用於創建其他兩個任務和一個初始值爲 0 的信號量,任務 C必須徵得任務 B 的同意才能執行一次操作。
答:這個問題顯然是一個任務同步的問題,在兩個任務之間設置一個初值爲 0 的信號量來實現兩個任務的合作。任務 B 通過發信號量表示同意與否,任務 C 一直請求信號量,當信號量大於1的時候任務C才能執行接下來的操作。

OS_SEM SYNC_SEM; //定義一個信號量,用於任務同步
//創建一個信號量
OSSemCreate ((OS_SEM* )&SYNC_SEM,
			 (CPU_CHAR* )"SYNC_SEM",
			 (OS_SEM_CTR )0,
			 (OS_ERR* )&err);
//任務 1 的任務函數
void task1_task(void *p_arg)
{
	u8 key;
	OS_ERR err;
	while(1)
	{
		key = KEY_Scan(0); //掃描按鍵
		if(key==WKUP_PRES)
		{
			OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);// 發送信號量 (1)
			LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); // 顯示信號量值 (2)
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err); //延時 10ms
	}
}
//任務 2 的任務函數
void task2_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	while(1)
	{
		// 請求信號量
		OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); (3)
		num++;
		LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); //顯示信號量值
		LCD_Fill(6,131,233,313,lcd_discolor[num%14]); //刷屏
		LED1 = ~LED1;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}

(1) 當 KEY_UP 鍵按下的時候調用 OSSemPost()函數發送一次信號量。
(2) 信號量 SYNC_SEM 的字段 Ctr 用來記錄信號量值,我們每調用一次 OSSemPost()函數Ctr 字段就會加一,這裏我們將 Ctr 的值顯示在 LCD 上,來觀察 Ctr 的變化。
(3) 任務 2 請求信號量 SYNC_SEM,如果請求到信號量的話就會執行任務 2 下面的代碼,如果沒有請求到的話就會一直阻塞函數。當調用函數 OSSemPend()請求信號量成功的話,SYNC_SEM 的字段 Ctr 就會減一,直到爲 0。在任務 2 中我們也將信號量 SYNC_SEM 的字段Ctr 顯示在 LCD 上,觀察其變化。

優先級反轉

優先級反轉在可剝奪內核中是非常常見的,在實時系統中不允許出現這種現象,這樣會破!!!z1!@壞任務的預期順序,可能會導致嚴重的後果
在這裏插入圖片描述
(1) 任務 H 和任務 M 處於掛起狀態,等待某一事件的發生,任務 L 正在運行。
(2) 某一時刻任務 L 想要訪問共享資源,在此之前它必須先獲得對應該資源的信號量。
(3) 任務 L 獲得信號量並開始使用該共享資源。
(4) 由於任務 H 優先級高,它等待的事件發生後便剝奪了任務 L 的 CPU 使用權。
(5) 任務 H 開始運行。
(6) 任務 H 運行過程中也要使用任務 L 正在使用着的資源,由於該資源的信號量還被任務
L 佔用着,任務 H 只能進入掛起狀態,等待任務 L 釋放該信號量。
(7) 任務 L 繼續運行。
(8) 由於任務 M 的優先級高於任務 L,當任務 M 等待的事件發生後,任務 M 剝奪了任務 L
的 CPU 使用權。
(9) 任務 M 處理該處理的事。
(10) 任務 M 執行完畢後,將 CPU 使用權歸還給任務 L。
(11) 任務 L 繼續運行。
(12) 最終任務 L 完成所有的工作並釋放了信號量,到此爲止,由於實時內核知道有個高優先級的任務在等待這個信號量,故內核做任務切換。
(13) 任務 H 得到該信號量並接着運行。在這種情況下,任務 H 的優先級實際上降到了任務 L 的優先級水平。因爲任務 H 要一直等待直到任務 L 釋放其佔用的那個共享資源。由於任務 M 剝奪了任務 L 的 CPU 使用權,使得任務 H 的情況更加惡化,這樣就相當於任務 M 的優先級高於任務 H,導致優先級反轉。

優先級反轉試驗
創建 4 個任務,任務 A 用於創建 B、C 和 D 這三個任務,A 還創建了一個初始值爲 1的信號量 TEST_SEM,任務 B 和 D 都請求信號量 TEST_SEM,其中任務優先級從高到底分別爲:B、C、D。

OS_SEM TEST_SEM; //定義一個信號量
//創建一個信號量
OSSemCreate ((OS_SEM* )&TEST_SEM,
			 (CPU_CHAR* )"TEST_SEM",
			 (OS_SEM_CTR )1, //信號量初始值爲 1
			 (OS_ERR* )&err);
//高優先級任務的任務函數
void high_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(5,110,115,314); //畫一個矩形
	LCD_DrawLine(5,130,115,130); //畫線
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"High Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延時 500ms
		num++;
		printf("high task Pend Sem\r\n");
		OSSemPend(&TEST_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //請求信號量(1)
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充區域
 		LED1 = ~LED1;
		OSSemPost(&TEST_SEM,OS_OPT_POST_1,&err); //釋放信號量(2)
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延時 500ms
	}
}
//中等優先級任務的任務函數
void middle_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(125,110,234,314); //畫一個矩形
	LCD_DrawLine(125,130,234,130); //畫線
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Middle Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		num++;
		printf("middle task Running!\r\n");
		LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充區域
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}
//低優先級任務的任務函數
void low_task(void *p_arg)
{
	static u32 times;
	OS_ERR err;
	while(1)
	{
		OSSemPend(&TEST_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //請求信號量(3)
		printf("low task Running!\r\n");
		for(times=0;times<20000000;times++) (4)
		{
			OSSched(); //發起任務調度
		}
		OSSemPost(&TEST_SEM,OS_OPT_POST_1,&err); //釋放信號量 (5)
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}

實驗結果:
在這裏插入圖片描述
(1)、low_task 任務獲得下信號量 TEST_SEM 開始運行。
(2)、high_task 請求信號量 TEST_SEM,但是此時信號量 TEST_SEM 被任務 low_task 佔用着,因此 high_task 就要一直等待,直到 low_task 任務釋放信號量 TEST_SEM。
(3)、由於high_task沒有請求到信號量TEST_SEM,只能一直等待,紅色部分代碼中high_task沒有運行,而middle_task一直在運行,給人的感覺就是middle_task的任務優先級高於high_task。但是事實上 high_task 任務的任務優先級是高於 middle_task 的,這個就是優先級反轉!
(4)、high_task 任務因爲獲得信號量 TEST_SEM 而運行從例-4 中可以看出,當一個低優先級任務和一個高優先級任務同時使用同一個信號量,而系統中還有其他中等優先級任務時。如果低優先級任務獲得了信號量,那麼高優先級的任務就會處於等待狀態,但是,中等優先級的任務可以打斷低優先級任務而先於高優先級任務運行(此時高優先級的任務在等待信號量 ,所以不能運行),這是就出現了優先級反轉的現象。

互斥信號量

爲了避免優先級反轉這個問題,UCOSIII 支持一種特殊的二進制信號量:互斥信號量, 用它可以解決優先級反轉問題
在這裏插入圖片描述
(1) 任務 H 與任務 M 處於掛起狀態,等待某一事件的發生,任務 L 正在運行中。
(2) 某一時刻任務 L 想要訪問共享資源,在此之前它必須先獲得對應資源的互斥型信號量。
(3) 任務 L 獲得互斥型信號量並開始使用該共享資源。
(4) 由於任務 H 優先級高,它等待的事件發生後便剝奪了任務 L 的 CPU 使用權。
(5) 任務 H 開始運行。
(6) 任務 H 運行過程中也要使用任務 L 在使用的資源,考慮到任務 L 正在佔用着資源,UCOSIII 會將任務 L 的優先級升至同任務 H 一樣,使得任務 L 能繼續執行而不被其他中等優先級的任務打斷。
(7) 任務 L 以任務 H 的優先級繼續運行,注意此時任務 H 並沒有運行,因爲任務 H 在等待任務 L 釋放掉互斥信號量。
(8) 任務 L 完成所有的任務,並釋放掉互斥型信號量,UCOSIII 會自動將任務 L 的優先級恢復到提升之前的值,然後 UCOSIII 會將互斥型信號量給正在等待着的任務 H。
(9) 任務 H 獲得互斥信號量開始執行。
(10) 任務 H 不再需要訪問共享資源,於是釋放掉互斥型信號量。
(11) 由於沒有更高優先級的任務需要執行,所以任務 H 繼續執行。
(12) 任務 H 完成所有工作,並等待某一事件發生,此時 UCOSIII 開始運行在任務 H 或者任務 L 運行過程中已經就緒的任務 M。
(13) 任務 M 繼續執行。
注意!只有任務才能使用互斥信號量(中斷服務程序則不可以),UCOSIII 允許用戶嵌套使用互斥型信號量,一旦一個任務獲得了一個互斥型信號量,則該任務最多可以對該互斥型信號量嵌套使用 250 次,當然該任務只有釋放相同的次數才能真正釋放這個互斥型信號量。
在這裏插入圖片描述
創建互斥信號量
OSMutexCreate()

void OSMutexCreate (OS_MUTEX *p_mutex,
					CPU_CHAR *p_name,
					OS_ERR *p_err)

p_mutex : 指向互斥型信號量控制塊。互斥型信號量必須有用戶應用程序進行實際分配,可以使用如下所示代碼。
OS_MUTEX MyMutex;
p_name : 互斥信號量的名字
p_err : 調用此函數後返回的錯誤碼

請求互斥型信號量
當一個任務需要對資源進行獨佔式訪問的時候就可以使用函數 OSMutexPend(),如果該互斥信號量正在被其他的任務使用,那麼 UCOSIII 就會將請求這個互斥信號量的任務放置在這個互斥信號量的等待表中。任務會一直等待,直到這個互斥信號量被釋放掉,或者設定的超時時間到達爲止。如果在設定的超時時間到達之前信號量被釋放,UCOSIII 將會恢復所有等待這個信號量的任務中優先級最高的任務。
注意!如果佔用該互斥信號量的任務比當前申請該互斥信號量的任務優先級低的話,OSMutexPend()函數會將佔用該互斥信號量的任務的優先級提升到和當前申請任務的優先級一樣。當佔用該互斥信號量的任務釋放掉該互斥信號量以後,恢復到之前的優先級。

void OSMutexPend (OS_MUTEX *p_mutex,
				  OS_TICK timeout,
				  OS_OPT opt,
				  CPU_TS *p_ts,
				  OS_ERR *p_err)

p_mutex : 指向互斥信號量。
timeout : 指定等待互斥信號量的超時時間(時鐘節拍數),如果在指定的時間內互斥信號量沒有釋放,則允許任務恢復執行。該值設置爲 0 的話,表示任務將會一直等待下去,直到信號量被釋放掉。
opt : 用於選擇是否使用阻塞模式。
OS_OPT_PEND_BLOCKING 指定互斥信號量被佔用時,任務掛起等待該互斥信號量。
OS_OPT_PEND_NON_BLOCKING 指定當互斥信號量被佔用時,直接返回
任務。
注意!當設置爲 OS_OPT_PEND_NON_BLOCKING,是 timeout 參數就沒有意義了,應該設置爲 0。
p_ts : 指向一個時間戳,記錄發送、終止或刪除互斥信號量的時刻。
p_err : 用於保存調用此函數後返回的錯誤碼。

發送互斥信號量
OSMutexPend()

void OSMutexPost (OS_MUTEX *p_mutex,
				  OS_OPT opt,
				  OS_ERR *p_err)

p_mutex: 指向互斥信號量。
opt: 用來指定是否進行任務調度操作
OS_OPT_POST_NONE 不指定特定的選項
OS_OPT_POST_NO_SCHED 禁止在本函數內執行任務調度操作。
p_err: 用來保存調用此函數返回的錯誤碼。

互斥信號量試

OS_MUTEX TEST_MUTEX; //定義一個互斥信號量
/創建一個互斥信號量
OSMutexCreate((OS_MUTEX* )&TEST_MUTEX,
              (CPU_CHAR* )"TEST_MUTEX",
			  (OS_ERR* )&err);
//高優先級任務的任務函數
void high_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(5,110,115,314); //畫一個矩形
	LCD_DrawLine(5,130,115,130); //畫線
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"High Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延時 500ms
		num++;
		printf("high task Pend Sem\r\n");
		OSMutexPend (&TEST_MUTEX,0,\ // 請求互斥信號量 (1)
		OS_OPT_PEND_BLOCKING,0,&err);
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充區域
		LED1 = ~LED1;
		OSMutexPost(&TEST_MUTEX,OS_OPT_POST_NONE,&err);// 釋放互斥信號量 (2)
		OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_PERIODIC,&err); //延時 500ms
	}
}
//中等優先級任務的任務函數
void middle_task(void *p_arg)
{
	u8 num;
	OS_ERR err;
	CPU_SR_ALLOC();
	POINT_COLOR = BLACK;
	OS_CRITICAL_ENTER();
	LCD_DrawRectangle(125,110,234,314); //畫一個矩形
	LCD_DrawLine(125,130,234,130); //畫線
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Middle Task");
	OS_CRITICAL_EXIT();
	while(1)
	{
		num++;
		printf("middle task Running!\r\n");
		LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充區域
		LED0 = ~LED0;
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}
//低優先級任務的任務函數
void low_task(void *p_arg)
{
	static u32 times;
	OS_ERR err;
	while(1)
	{
		OSMutexPend (&TEST_MUTEX,0,\ // 請求互斥信號量 (3)
		OS_OPT_PEND_BLOCKING,0,&err);
		printf("low task Running!\r\n");
		for(times=0;times<20000000;times++) (4)
		{
			OSSched(); // 發起任務調度
		}
		OSMutexPost(&TEST_MUTEX,\ (5)
		OS_OPT_POST_NONE,&err);// 釋放互斥信號量
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延時 1s
	}
}

(1)、high_task 任務中請求互斥信號量 TEST_MUTEX。
(2)、high_task 任務中釋放互斥信號量 TEST_MUTEX。
(3)、low_task 任務中請求會互斥信號量 TEST_MUTEX。
(4)、這裏用來模擬 low_task 任務長時間佔用互斥信號量 TEST_MUTEX。
(5)、low_task 任務中釋放互斥信號量 TEST_MUTEX。
在這裏插入圖片描述
(1)、middle_task 任務運行。
(2)、low_task 獲得互斥信號量運行。
(3)、high_task 請求信號量,在這裏會等待一段時間,等待 low_task 任務釋放互斥信號量。但是 middle_task 不會運行,因爲由於 low_task 正在使用互斥信號量,所以 low_task 任務優先級暫時提升到了一個高優先級(比 middle_task 任務優先級高),所以 middle_task 任務不能在打斷low_task 任務的運行了!
(4)、high_task 任務獲得互斥信號量而運行。從上面的分析可以看出互斥信號量有效的抑制了優先級反轉現象的發生。

任務內嵌信號量

前面我們使用信號量時都需要先創建一個信號量,不過在 UCOSIII 中每個任務都有自己的內嵌的信號量,這種功能不僅能夠簡化代碼,而且比使用獨立的信號量更有效。任務信號量是直接內嵌在 UCOSIII 中的,任務信號量相關代碼在 os_task.c 中的。
等待任務信號量
OSTaskSemPend()

OS_SEM_CTR OSTaskSemPend ( OS_TICK timeout,
						   OS_OPT opt,
						   CPU_TS *p_ts,
						   OS_ERR *p_err)

timeout : 如果在指定的節拍數內沒有收到信號量任務就會因爲等待超時而恢復運行,如果 timeout 爲 0 的話任務就會一直等待,直到收到信號量。
opt: 用於選擇是否使用阻塞模式。
OS_OPT_PEND_BLOCKING 指定互斥信號量被佔用時,任務掛起等待該互斥信號量。
OS_OPT_PEND_NON_BLOCKING 指定當互斥信號量被佔用時,直接返回任務。
注意!當設置爲 OS_OPT_PEND_NON_BLOCKING,是 timeout 參數就沒有意義了,應該設置爲 0。
p_ts : 指向一個時間戳,記錄發送、終止或刪除互斥信號量的時刻。
P_err: 調用此函數後返回的錯誤碼。

發佈任務信號量
OSTaskSemPost()

OS_SEM_CTR OSTaskSemPost (OS_TCB *p_tcb,
						  OS_OPT opt,
						  OS_ERR *p_err

p_tcb : 指向要用信號通知的任務的 TCB,當設置爲 NULL 的時候可以向自己發送信
量。
opt: 用來指定是否進行任務調度操作
OS_OPT_POST_NONE 不指定特定的選項
OS_OPT_POST_NO_SCHED 禁止在本函數內執行任務調度操作。
p_err: 調用此函數後返回的錯誤碼。

發佈了74 篇原創文章 · 獲贊 65 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章