1. 程式人生 > >【UCOSIII】UCOSIII的互斥訊號量

【UCOSIII】UCOSIII的互斥訊號量

訊號量用於控制對共享資源的保護,但是現在基本用來做任務同步用(不太清楚的可以參考連結:【UCOSIII】UCOSIII的訊號量)。

優先順序反轉

優先順序反轉在可剝奪核心中是非常常見的,在實時系統中不允許出現這種現象,這樣會破壞任務的預期順序,可能會導致嚴重的後果,下圖就是一個優先順序反轉的例子:


關於這個優先順序反轉的例子,先來進行分析一下:

  1. 任務H和任務M起初處於掛起狀態,等待某一事件的發生,而任務L正在執行;
  2. 某一時刻任務L想要訪問共享資源,在此之前它必須先獲得對應該資源的訊號量;
  3. 任務L獲得訊號量並開始使用該共享資源;
  4. 由於任務H優先順序高,它等待的事件發生後便剝奪了任務L的CPU使用權(或者說,任務L呼叫了延時函式,發生任務排程,任務H此時是最高優先順序);
  5. 任務H開始執行;
  6. 任務H執行過程中也要使用任務L正在使用著的資源,由於該資源的訊號量還被任務L佔用著,任務H只能進入掛起狀態,等待任務L釋放該訊號量;
  7. 任務L繼續執行;
  8. 由於任務M的優先順序高於任務L,當任務M等待的事件發生後,任務M剝奪了任務L的CPU使用權(或者說,任務L又呼叫了延時函式,發生任務排程,任務M此時是最高優先順序);
  9. 任務M處理該處理的事;
  10. 任務M執行完畢後,將CPU使用權歸還給任務L;
  11. 任務L繼續執行;
  12. 最終任務L完成所有的工作並釋放了訊號量,到此為止,由於實時核心知道有個高優先順序的任務在等待這個訊號量,故核心做任務切換;
  13. 任務H得到該訊號量並接著執行。

在這種情況下,任務H的優先順序實際上降到了任務L的優先順序水平。因為任務H要一直等待直到任務L釋放其佔用的那個共享資源。由於任務M剝奪了任務L的CPU使用權,使得任務H的情況更加惡化,這樣就相當於任務M的優先順序高於任務H,導致優先順序反轉。


解決這個問題大辦法就是:提升優先順序。當由於訊號量被任務L佔用而導致任務H被掛起之後,將任務L的優先順序暫時提升至任務H的優先順序,直到任務L釋放訊號量。也就是說,將擁有訊號量的任務優先順序暫時提升至目前正在等待該訊號量的任務中的最高優先順序!這樣,就不會被比等待該訊號量的任務的優先順序低的任務,強行剝奪擁有訊號量的任務,導致優先順序反轉。

互斥訊號量

遵循解決優先順序反轉的一般解決思路,UCOSIII支援一種特殊的二進位制訊號量:互斥訊號量,用它可以解決優先順序反轉問題,如下圖所示:


同樣,對於這個流程也進行分析一下:

  1. 任務H和任務M起初處於掛起狀態,等待某一事件的發生,而任務L正在執行;
  2. 某一時刻任務L想要訪問共享資源,在此之前它必須先獲得對應該資源的訊號量;
  3. 任務L獲得訊號量並開始使用該共享資源;
  4. 由於任務H優先順序高,它等待的事件發生後便剝奪了任務L的CPU使用權(或者說,任務L呼叫了延時函式,發生任務排程,任務H此時是最高優先順序);
  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次,當然該任務只有釋放相同的次數才能真正釋放這個互斥型訊號量。

至於互斥訊號量的思想:將擁有訊號量的任務優先順序暫時提升至目前正在等待該訊號量的任務中的最高優先順序!

互斥訊號量API函式

互斥訊號量API函式
函式說明
OSMutexCreate()建立一個互斥訊號量
OSMutexDel()刪除一個互斥型訊號量
OSMutexPend()等待一個互斥型訊號量
OSMutexPendAbort()取消等待
OSMutexPost()釋放一個互斥型訊號量

建立互斥型訊號量

建立互斥訊號量使用函式OSMutexCreate(),函式原型如下:

void  OSMutexCreate (OS_MUTEX  *p_mutex,                        //指向互斥型訊號量控制塊
                     CPU_CHAR  *p_name,                            //互斥訊號量的名字
                     OS_ERR    *p_err)
{
    CPU_SR_ALLOC();

    OS_CRITICAL_ENTER();
    p_mutex->Type              =  OS_OBJ_TYPE_MUTEX;        /* Mark the data structure as a mutex                     */
    p_mutex->NamePtr           =  p_name;
    p_mutex->OwnerTCBPtr       = (OS_TCB       *)0;
    p_mutex->OwnerNestingCtr   = (OS_NESTING_CTR)0;         /* Mutex is available                                     */
    p_mutex->TS                = (CPU_TS        )0;
    p_mutex->OwnerOriginalPrio =  OS_CFG_PRIO_MAX;
    OS_PendListInit(&p_mutex->PendList);                    /* Initialize the waiting list                            */

    OSMutexQty++;

    OS_CRITICAL_EXIT_NO_SCHED();
   *p_err = OS_ERR_NONE;
}

互斥訊號量控制塊是什麼結構呢?

struct  os_mutex {                                          /* Mutual Exclusion Semaphore                             */
                                                            /* ------------------ GENERIC  MEMBERS ------------------ */
    OS_OBJ_TYPE          Type;                              /* Should be set to OS_OBJ_TYPE_MUTEX                     */
    CPU_CHAR            *NamePtr;                           /* Pointer to Mutex Name (NUL terminated ASCII)           */
    OS_PEND_LIST         PendList;                          /* List of tasks waiting on mutex                         */
#if OS_CFG_DBG_EN > 0u
    OS_MUTEX            *DbgPrevPtr;
    OS_MUTEX            *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
                                                            /* ------------------ SPECIFIC MEMBERS ------------------ */
    OS_TCB              *OwnerTCBPtr;
    OS_PRIO              OwnerOriginalPrio;
    OS_NESTING_CTR       OwnerNestingCtr;                   /* Mutex is available when the counter is 0               */
    CPU_TS               TS;
};

可以看出,互斥訊號量是一個二進位制訊號量,並沒有用於記錄當前訊號量的值的成員變數Ctr。

請求互斥型訊號量

當一個任務需要對資源進行獨佔式訪問的時候就可以使用函式OSMutexPend(),如果該互斥訊號量正在被其他的任務使用,那麼UCOSIII就會將請求這個互斥訊號量的任務放置在這個互斥訊號量的等待表中。任務會一直等待,直到這個互斥訊號量被釋放掉,或者設定的超時時間到達為止。如果在設定的超時時間到達之前訊號量被釋放,UCOSIII將會恢復所有等待這個訊號量的任務中優先順序最高的任務。

注意!如果佔用該互斥訊號量的任務比當前申請該互斥訊號量的任務優先順序低的話,OSMutexPend()函式會將佔用該互斥訊號量的任務的優先順序提升到和當前申請任務的優先順序一樣。當佔用該互斥訊號量的任務釋放掉該互斥訊號量以後,恢復到之前的優先順序。OSMutexPend()函式原型如下:

void  OSMutexPend (OS_MUTEX  *p_mutex,                        //指向互斥訊號量
                   OS_TICK    timeout,                        //指定等待互斥訊號量的超時時間(時鐘節拍數)
                   OS_OPT     opt,                           //用於選擇是否使用阻塞模式
                   CPU_TS    *p_ts,                            //指向一個時間戳
                   OS_ERR    *p_err)
{
    OS_PEND_DATA  pend_data;
    OS_TCB       *p_tcb;
    CPU_SR_ALLOC();

    if (p_ts != (CPU_TS *)0) {
       *p_ts  = (CPU_TS  )0;                                /* Initialize the returned timestamp                      */
    }

    CPU_CRITICAL_ENTER();
    if (p_mutex->OwnerNestingCtr == (OS_NESTING_CTR)0) {    /* Resource available?                                    */
        p_mutex->OwnerTCBPtr       =  OSTCBCurPtr;          /* Yes, caller may proceed                                */
        p_mutex->OwnerOriginalPrio =  OSTCBCurPtr->Prio;
        p_mutex->OwnerNestingCtr   = (OS_NESTING_CTR)1;
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = p_mutex->TS;
        }
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_NONE;
        return;
    }

    if (OSTCBCurPtr == p_mutex->OwnerTCBPtr) {              /* See if current task is already the owner of the mutex  */
        p_mutex->OwnerNestingCtr++;
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = p_mutex->TS;
        }
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_MUTEX_OWNER;                         /* Indicate that current task already owns the mutex      */
        return;
    }

    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_PEND_WOULD_BLOCK;                    /* No                                                     */
        return;
    } else {
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_SCHED_LOCKED;
            return;
        }
    }

    OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */
    p_tcb = p_mutex->OwnerTCBPtr;                           /* Point to the TCB of the Mutex owner                    */
    if (p_tcb->Prio > OSTCBCurPtr->Prio) {                  /* See if mutex owner has a lower priority than current   */
        switch (p_tcb->TaskState) {
            case OS_TASK_STATE_RDY:
                 OS_RdyListRemove(p_tcb);                   /* Remove from ready list at current priority             */
                 p_tcb->Prio = OSTCBCurPtr->Prio;           /* Raise owner's priority                                 */
                 OS_PrioInsert(p_tcb->Prio);
                 OS_RdyListInsertHead(p_tcb);               /* Insert in ready list at new priority                   */
                 break;

            case OS_TASK_STATE_DLY:
            case OS_TASK_STATE_DLY_SUSPENDED:
            case OS_TASK_STATE_SUSPENDED:
                 p_tcb->Prio = OSTCBCurPtr->Prio;           /* Only need to raise the owner's priority                */
                 break;

            case OS_TASK_STATE_PEND:                        /* Change the position of the task in the wait list       */
            case OS_TASK_STATE_PEND_TIMEOUT:
            case OS_TASK_STATE_PEND_SUSPENDED:
            case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
                 OS_PendListChangePrio(p_tcb,
                                       OSTCBCurPtr->Prio);
                 break;

            default:
                 OS_CRITICAL_EXIT();
                *p_err = OS_ERR_STATE_INVALID;
                 return;
        }
    }

    OS_Pend(&pend_data,                                     /* Block task pending on Mutex                            */
            (OS_PEND_OBJ *)((void *)p_mutex),
             OS_TASK_PEND_ON_MUTEX,
             timeout);

    OS_CRITICAL_EXIT_NO_SCHED();

    OSSched();                                              /* Find the next highest priority task ready to run       */

    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                             /* We got the mutex                                       */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_NONE;
             break;

        case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_PEND_ABORT;
             break;

        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get mutex within timeout       */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = (CPU_TS  )0;
             }
            *p_err = OS_ERR_TIMEOUT;
             break;

        case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_OBJ_DEL;
             break;

        default:
            *p_err = OS_ERR_STATUS_INVALID;
             break;
    }
    CPU_CRITICAL_EXIT();
}

timeout:指定等待互斥訊號量的超時時間(時鐘節拍數),如果在指定的時間內互斥訊號量沒有釋放,則允許任務恢復執行。該值設定為0的話,表示任務將會一直等待下去,直到訊號量被釋放掉。

opt:用於選擇是否使用阻塞模式,有下面兩個選項。OS_OPT_PEND_BLOCKING:指定互斥訊號量被佔用時,任務掛起等待該互斥訊號量;OS_OPT_PEND_NON_BLOCKING:指定當互斥訊號量被佔用時,直接返回任務。

注意!當設定為OS_OPT_PEND_NON_BLOCKING,是timeout引數就沒有意義了,應該設定為0。

傳送互斥訊號量

我們可以通過呼叫函式OSMutexPost()來釋放互斥型訊號量,只有之前呼叫過函式OSMutexPend()獲取互斥訊號量,才需要呼叫OSMutexPost()函式來釋放這個互斥訊號量,函式原型如下:

void  OSMutexPost (OS_MUTEX  *p_mutex,                    //指向互斥訊號量
                   OS_OPT     opt,                    //用來指定是否進行任務排程操作
                   OS_ERR    *p_err)
{
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    CPU_TS         ts;
    CPU_SR_ALLOC();

    CPU_CRITICAL_ENTER();
    if (OSTCBCurPtr != p_mutex->OwnerTCBPtr) {              /* Make sure the mutex owner is releasing the mutex       */
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_MUTEX_NOT_OWNER;
        return;
    }

    OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
    ts          = OS_TS_GET();                              /* Get timestamp                                          */
    p_mutex->TS = ts;
    p_mutex->OwnerNestingCtr--;                             /* Decrement owner's nesting counter                      */
    if (p_mutex->OwnerNestingCtr > (OS_NESTING_CTR)0) {     /* Are we done with all nestings?                         */
        OS_CRITICAL_EXIT();                                 /* No                                                     */
       *p_err = OS_ERR_MUTEX_NESTING;
        return;
    }

    p_pend_list = &p_mutex->PendList;
    if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) {         /* Any task waiting on mutex?                             */
        p_mutex->OwnerTCBPtr     = (OS_TCB       *)0;       /* No                                                     */
        p_mutex->OwnerNestingCtr = (OS_NESTING_CTR)0;
        OS_CRITICAL_EXIT();
       *p_err = OS_ERR_NONE;
        return;
    }
                                                            /* Yes                                                    */
    if (OSTCBCurPtr->Prio != p_mutex->OwnerOriginalPrio) {
        OS_RdyListRemove(OSTCBCurPtr);
        OSTCBCurPtr->Prio = p_mutex->OwnerOriginalPrio;     /* Lower owner's priority back to its original one        */
        OS_PrioInsert(OSTCBCurPtr->Prio);
        OS_RdyListInsertTail(OSTCBCurPtr);                  /* Insert owner in ready list at new priority             */
        OSPrioCur         = OSTCBCurPtr->Prio;
    }
                                                            /* Get TCB from head of pend list                         */
    p_tcb                      = p_pend_list->HeadPtr->TCBPtr;
    p_mutex->OwnerTCBPtr       = p_tcb;                     /* Give mutex to new owner                                */
    p_mutex->OwnerOriginalPrio = p_tcb->Prio;
    p_mutex->OwnerNestingCtr   = (OS_NESTING_CTR)1;
                                                            /* Post to mutex                                          */
    OS_Post((OS_PEND_OBJ *)((void *)p_mutex),
            (OS_TCB      *)p_tcb,
            (void        *)0,
            (OS_MSG_SIZE  )0,
            (CPU_TS       )ts);

    OS_CRITICAL_EXIT_NO_SCHED();

    if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {
        OSSched();                                          /* Run the scheduler                                      */
    }

   *p_err = OS_ERR_NONE;
}

opt:用來指定是否進行任務排程操作,有以下兩個選項。OS_OPT_POST_NONE:不指定特定的選項;OS_OPT_POST_NO_SCHED:禁止在本函式內執行任務排程操作。

UCOSIII實際例程

在互斥訊號量實驗之前,首先先來一個優先順序反轉的例項。

優先順序反轉實驗

例程要求:建立4個任務,任務A用於建立B、C和D這三個任務,A還建立了一個初始值為1的訊號量TEST_SEM,任務B和D都請求訊號量TEST_SEM,其中任務優先順序從高到底分別為:B、C、D。

例子:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "includes.h"

//UCOSIII中以下優先順序使用者程式不能使用,ALIENTEK
//將這些優先順序分配給了UCOSIII的5個系統內部任務
//優先順序0:中斷服務服務管理任務 OS_IntQTask()
//優先順序1:時鐘節拍任務 OS_TickTask()
//優先順序2:定時任務 OS_TmrTask()
//優先順序OS_CFG_PRIO_MAX-2:統計任務 OS_StatTask()
//優先順序OS_CFG_PRIO_MAX-1:空閒任務 OS_IdleTask()

//任務優先順序
#define START_TASK_PRIO			10
//任務堆疊大小	
#define START_STK_SIZE 			128
//任務控制塊
OS_TCB StartTaskTCB;
//任務堆疊	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任務函式
void start_task(void *p_arg);

//任務優先順序
#define HIGH_TASK_PRIO			7
//任務堆疊大小	
#define HIGH_STK_SIZE 			128
//任務控制塊
OS_TCB High_TaskTCB;
//任務堆疊	
CPU_STK HIGH_TASK_STK[HIGH_STK_SIZE];
void high_task(void *p_arg);

//任務優先順序
#define MIDDLE_TASK_PRIO		8
//任務堆疊大小	
#define MIDDLE_STK_SIZE 		128
//任務控制塊
OS_TCB Middle_TaskTCB;
//任務堆疊	
CPU_STK MIDDLE_TASK_STK[MIDDLE_STK_SIZE];
void middle_task(void *p_arg);

//任務優先順序
#define LOW_TASK_PRIO			9
//任務堆疊大小	
#define LOW_STK_SIZE 			128
//任務控制塊
OS_TCB Low_TaskTCB;
//任務堆疊	
CPU_STK LOW_TASK_STK[LOW_STK_SIZE];
void low_task(void *p_arg);

//LCD刷屏時使用的顏色
int lcd_discolor[14]={	WHITE, BLACK, BLUE,  BRED,      
						GRED,  GBLUE, RED,   MAGENTA,       	 
						GREEN, CYAN,  YELLOW,BROWN, 			
						BRRED, GRAY };

OS_SEM	TEST_SEM;		//定義一個訊號量

int main(void)                    //主函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  //時鐘初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中斷分組配置
	uart_init(115200);   //串列埠初始化
	LED_Init();         //LED初始化	
	LCD_Init();			//LCD初始化	
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-4");
	LCD_ShowString(30,50,200,16,16,"Prio Inversion");
	LCD_ShowString(30,70,200,16,16,"[email protected]");
	LCD_ShowString(30,90,200,16,16,"2015/5/28");
	
	OSInit(&err);		    //初始化UCOSIII
	OS_CRITICAL_ENTER();	//進入臨界區			 
	//建立開始任務寸vs:v
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任務控制塊
		 (CPU_CHAR	* )"start task", 		//任務名字
                 (OS_TASK_PTR )start_task, 			//任務函式
                 (void		* )0,					//傳遞給任務函式的引數
                 (OS_PRIO	  )START_TASK_PRIO,     //任務優先順序
                 (CPU_STK   * )&START_TASK_STK[0],	//任務堆疊基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//任務堆疊深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,		//任務堆疊大小
                 (OS_MSG_QTY  )0,					//任務內部訊息佇列能夠接收的最大訊息數目,為0時禁止接收訊息
                 (OS_TICK	  )0,					//當使能時間片輪轉時的時間片長度,為0時為預設長度,
                 (void   	* )0,					//使用者補充的儲存區
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任務選項
                 (OS_ERR 	* )&err);				//存放該函式錯誤時的返回值
	OS_CRITICAL_EXIT();	//退出臨界區	 
	OSStart(&err);      //開啟UCOSIII
}

void start_task(void *p_arg)                //開始任務函式
{    
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//統計任務                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了測量中斷關閉時間
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //當使用時間片輪轉的時候
	 //使能時間片輪轉排程功能,時間片長度為1個系統時鐘節拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif	
		
	OS_CRITICAL_ENTER();	//進入臨界區

	OSSemCreate ((OS_SEM*	)&TEST_SEM,            //建立一個訊號量
                 (CPU_CHAR*	)"TEST_SEM",
                 (OS_SEM_CTR)1,				//訊號量初始值為1
                 (OS_ERR*	)&err);

	OSTaskCreate((OS_TCB 	* )&High_TaskTCB,	        //建立HIGH任務	
				 (CPU_CHAR	* )"High task", 		
                 (OS_TASK_PTR )high_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )HIGH_TASK_PRIO,     
                 (CPU_STK   * )&HIGH_TASK_STK[0],	
                 (CPU_STK_SIZE)HIGH_STK_SIZE/10,	
                 (CPU_STK_SIZE)HIGH_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);			

	OSTaskCreate((OS_TCB 	* )&Middle_TaskTCB,	            //建立MIDDLE任務	
				 (CPU_CHAR	* )"Middle task", 		
                 (OS_TASK_PTR )middle_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )MIDDLE_TASK_PRIO,     
                 (CPU_STK   * )&MIDDLE_TASK_STK[0],	
                 (CPU_STK_SIZE)MIDDLE_STK_SIZE/10,	
                 (CPU_STK_SIZE)MIDDLE_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);		

	OSTaskCreate((OS_TCB 	* )&Low_TaskTCB,	            //建立LOW任務	
				 (CPU_CHAR	* )"Low task", 		
                 (OS_TASK_PTR )low_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )LOW_TASK_PRIO,     
                 (CPU_STK   * )&LOW_TASK_STK[0],	
                 (CPU_STK_SIZE)LOW_STK_SIZE/10,	
                 (CPU_STK_SIZE)LOW_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);					 
	OS_CRITICAL_EXIT();	//退出臨界區
	OSTaskDel((OS_TCB*)0,&err);	//刪除start_task任務自身
}

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); 	//請求訊號量
		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);				//釋放訊號量
		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);     //請求訊號量
		printf("low task Running!\r\n");
		for(times=0;times<10000000;times++)
		{
			OSSched();			//發起任務排程
		}
		OSSemPost(&TEST_SEM,OS_OPT_POST_1,&err);	
		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,只能一直等待,而middle_task一直在執行,給人的感覺就是middle_task的任務優先順序高於high_task。但是事實上high_task任務的任務優先順序是高於middle_task的,這個就是優先順序反轉!

high_task任務因為獲得訊號量TEST_SEM而執行從上例中可以看出,當一個低優先順序任務和一個高優先順序任務同時使用同一個訊號量,而系統中還有其他中等優先順序任務時。如果低優先順序任務獲得了訊號量,那麼高優先順序的任務就會處於等待狀態,但是,中等優先順序的任務可以打斷低優先順序任務而先於高優先順序任務執行(此時高優先順序的任務在等待訊號量,所以不能執行),這是就出現了優先順序反轉的現象。

互斥訊號量實驗

在上例中由於使用了訊號量導致了優先順序反轉發生,下例中我們將訊號量換成互斥訊號量。

例子:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "includes.h"

//UCOSIII中以下優先順序使用者程式不能使用,ALIENTEK
//將這些優先順序分配給了UCOSIII的5個系統內部任務
//優先順序0:中斷服務服務管理任務 OS_IntQTask()
//優先順序1:時鐘節拍任務 OS_TickTask()
//優先順序2:定時任務 OS_TmrTask()
//優先順序OS_CFG_PRIO_MAX-2:統計任務 OS_StatTask()
//優先順序OS_CFG_PRIO_MAX-1:空閒任務 OS_IdleTask()

//任務優先順序
#define START_TASK_PRIO			10
//任務堆疊大小	
#define START_STK_SIZE 			128
//任務控制塊
OS_TCB StartTaskTCB;
//任務堆疊	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任務函式
void start_task(void *p_arg);

//任務優先順序
#define HIGH_TASK_PRIO			7
//任務堆疊大小	
#define HIGH_STK_SIZE 			128
//任務控制塊
OS_TCB High_TaskTCB;
//任務堆疊	
CPU_STK HIGH_TASK_STK[HIGH_STK_SIZE];
void high_task(void *p_arg);

//任務優先順序
#define MIDDLE_TASK_PRIO		8
//任務堆疊大小	
#define MIDDLE_STK_SIZE 		128
//任務控制塊
OS_TCB Middle_TaskTCB;
//任務堆疊	
CPU_STK MIDDLE_TASK_STK[MIDDLE_STK_SIZE];
void middle_task(void *p_arg);

//任務優先順序
#define LOW_TASK_PRIO			9
//任務堆疊大小	
#define LOW_STK_SIZE 			128
//任務控制塊
OS_TCB Low_TaskTCB;
//任務堆疊	
CPU_STK LOW_TASK_STK[LOW_STK_SIZE];
void low_task(void *p_arg);

//LCD刷屏時使用的顏色
int lcd_discolor[14]={	WHITE, BLACK, BLUE,  BRED,      
						GRED,  GBLUE, RED,   MAGENTA,       	 
						GREEN, CYAN,  YELLOW,BROWN, 			
						BRRED, GRAY };

OS_MUTEX	TEST_MUTEX;		//定義一個互斥訊號量

int main(void)                            //主函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  //時鐘初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中斷分組配置
	uart_init(115200);   //串列埠初始化
	LED_Init();         //LED初始
	LCD_Init();			//LCD初始化	
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-5");
	LCD_ShowString(30,50,200,16,16,"Mutex test");
	LCD_ShowString(30,70,200,16,16,"[email protected]");
	LCD_ShowString(30,90,200,16,16,"2015/5/28");
	
	OSInit(&err);		    //初始化UCOSIII
	OS_CRITICAL_ENTER();	//進入臨界區			 
	//建立開始任務
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任務控制塊
				 (CPU_CHAR	* )"start task", 		//任務名字
                 (OS_TASK_PTR )start_task, 			//任務函式
                 (void		* )0,					//傳遞給任務函式的引數
                 (OS_PRIO	  )START_TASK_PRIO,     //任務優先順序
                 (CPU_STK   * )&START_TASK_STK[0],	//任務堆疊基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//任務堆疊深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,		//任務堆疊大小
                 (OS_MSG_QTY  )0,					//任務內部訊息佇列能夠接收的最大訊息數目,為0時禁止接收訊息
                 (OS_TICK	  )0,					//當使能時間片輪轉時的時間片長度,為0時為預設長度,
                 (void   	* )0,					//使用者補充的儲存區
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任務選項
                 (OS_ERR 	* )&err);				//存放該函式錯誤時的返回值
	OS_CRITICAL_EXIT();	//退出臨界區	 
	OSStart(&err);      //開啟UCOSIII
}

void start_task(void *p_arg)                //開始任務函式
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	//統計任務                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		//如果使能了測量中斷關閉時間
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN  //當使用時間片輪轉的時候
	 //使能時間片輪轉排程功能,時間片長度為1個系統時鐘節拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif	
		
	OS_CRITICAL_ENTER();	//進入臨界區

	OSMutexCreate((OS_MUTEX*	)&TEST_MUTEX,                //建立一個互斥訊號量
				  (CPU_CHAR*	)"TEST_MUTEX",
                  (OS_ERR*		)&err);

	OSTaskCreate((OS_TCB 	* )&High_TaskTCB,		            //建立HIGH任務
				 (CPU_CHAR	* )"High task", 		
                 (OS_TASK_PTR )high_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )HIGH_TASK_PRIO,     
                 (CPU_STK   * )&HIGH_TASK_STK[0],	
                 (CPU_STK_SIZE)HIGH_STK_SIZE/10,	
                 (CPU_STK_SIZE)HIGH_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);			

	OSTaskCreate((OS_TCB 	* )&Middle_TaskTCB,	                //建立MIDDLE任務	
				 (CPU_CHAR	* )"Middle task", 		
                 (OS_TASK_PTR )middle_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )MIDDLE_TASK_PRIO,     
                 (CPU_STK   * )&MIDDLE_TASK_STK[0],	
                 (CPU_STK_SIZE)MIDDLE_STK_SIZE/10,	
                 (CPU_STK_SIZE)MIDDLE_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);		

	OSTaskCreate((OS_TCB 	* )&Low_TaskTCB,		//建立LOW任務
				 (CPU_CHAR	* )"Low task", 		
                 (OS_TASK_PTR )low_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )LOW_TASK_PRIO,     
                 (CPU_STK   * )&LOW_TASK_STK[0],	
                 (CPU_STK_SIZE)LOW_STK_SIZE/10,	
                 (CPU_STK_SIZE)LOW_STK_SIZE,		
                 (OS_MSG_QTY  )0,					
                 (OS_TICK	  )0,  					
                 (void   	* )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR 	* )&err);					 
	OS_CRITICAL_EXIT();	//退出臨界區
	OSTaskDel((OS_TCB*)0,&err);	//刪除start_task任務自身
}

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 Mutex\r\n");
		OSMutexPend (&TEST_MUTEX,0,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);				//釋放互斥訊號量
		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,OS_OPT_PEND_BLOCKING,0,&err);//請求互斥訊號量
		printf("low task Running!\r\n");
		for(times=0;times<10000000;times++)
		{
			OSSched();											//發起任務排程
		}
		OSMutexPost(&TEST_MUTEX,OS_OPT_POST_NONE,&err);			//釋放互斥訊號量
		OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);   	//延時1s
	}
}

相關推薦

UCOSIIIUCOSIII互斥訊號

訊號量用於控制對共享資源的保護,但是現在基本用來做任務同步用(不太清楚的可以參考連結:【UCOSIII】UCOSIII的訊號量)。優先順序反轉優先順序反轉在可剝奪核心中是非常常見的,在實時系統中不允許出現這種現象,這樣會破壞任務的預期順序,可能會導致嚴重的後果,下圖就是一個優

UCOSIIIUCOSIII訊號

訊號量訊號量像是一種上鎖機制,程式碼必須獲得對應的鑰匙才能繼續執行,一旦獲得了鑰匙,也就意味著該任務具有進入被鎖部分程式碼的許可權。一旦執行至被鎖程式碼段,則任務一直等待,直到對應被鎖部分程式碼的鑰匙被再次釋放才能繼續執行。訊號量用於控制對共享資源的保護,但是現在基本用來做任

UCOSIII訊號互斥訊號

在UCOSIII中可能會偶多個任務會訪問共享資源,因此訊號量最早用來控制任務存取共享資源,現在訊號量也被用來實現任務間的同步以及任務和ISP同步。在可剝奪的核心中,當任務獨佔式使用共享資源的時候,會低優的任務高於高階優先任務執行的現象,這個現象叫做優先順序反轉,

ucosIII 共享資源(訊號互斥訊號

共享資源: 變數(靜態或全域性變數)、資料結構體、RAM表格、I/O裝置等。OS在使用一些資源時候,例如IO裝置印表機,當任務1在使用印表機時候必須保證資源獨享,避免其他任務修改列印內容導致出錯,因此需要有資源共享機制。 一般推薦使用互斥訊號量對共享資源實現

UCOSIIIUCOSIII的任務排程和切換

UCOSIII任務排程可剝奪型任務排程任務排程就是中止當前正在執行的任務轉而去執行其他的任務。UCOSIII是可剝奪型核心,因此當一個高優先順序的任務準備就緒,並且此時發生了任務排程,那麼這個高優先順序的任務就會獲得CPU的使用權!UCOSIII中的任務排程是由任務排程器來完

UCOSIIIUCOSIII的同時等待多個核心物件

UCOSIII同時等待多個核心物件前面講述了UCOSIII的訊號量(一個任務與另一個任務同步)、事件標誌組(一個任務與多個任務同步),它們都可以完成任務的同步。同時,訊號量(保證全域性變數)、訊息佇列,它們都可以完成訊息的傳遞。但是,它們描述的情況都是任務如何等待單個物件,比

UCOSIIIUCOSIII的儲存管理

UCOSIII記憶體管理簡介作為一個RTOS作業系統,記憶體管理是必備的功能,因此UCOSIII也就記憶體管理能力。通常應用程式可以呼叫ANSI C編譯器的malloc()和free()函式來動態的分配和釋放記憶體,但是在嵌入式事實作業系統中最好不要這麼做,多次這樣的操作會把

UCOSIIIUCOSIII系統內部任務

之前講到UCOSIII預設有5個系統任務:空閒任務:UCOSIII建立的第一個任務,UCOSIII必須建立的任務,此任務有UCOSIII自動建立,不需要使用者手動建立;時鐘節拍任務:此任務也是必須建立的任務;統計任務:可選任務,用來統計CPU使用率和各個任務的堆疊使用量。此任

UCOSIIIUCOSIII軟體定時器

在學習STM32的時候會使用定時器來做很多定時任務,這個定時器是微控制器自帶的,也就是硬體定時器,在UCOSIII中提供了軟體定時器,我們可以使用這些軟體定時器完成一些功能,本文我們就講解一下UCOSIII軟體定時器。UCOSIII軟體定時器簡介定時器其實就是一個遞減計數器,

4FreeRTOS完整的分析一下程式碼二值訊號

1,主要的就是分析下面的程式碼 /* Includes ------------------------------------------------------------------*/ #include "main.h" //包含了三個標頭

Python多程序,同步互斥,訊號,鎖補充上一篇文章

from multiprocessing import Event,Process from time import sleep def wait_event1(): print("1想操作臨界區資源") e.wait() print("1開始操作臨界區資源",e.is_set()

181102VC++ DrawCli氏繪圖,填充、直線等效果集原始碼

DrawCli原始碼,綜合了氏量繪圖、畫圓形、橢圓形、三角形等多種規則的圖形,可填充圖案、繪製直線、曲線、設定圖形組合順序等各種畫圖功能,確實是不錯的繪圖例子。 原始碼下載地址:點選下載 備用下載地

互斥訊號

---關鍵程式碼如下--- void CTestSemaphoreDlg::OnBnClickedButtonThread1() {     // TODO: 在此新增控制元件通知處理程式程式碼     AfxBeginThread((AFX_THREADPROC)thre

uc/os-iii學習筆記-資源管理(中斷、訊號訊號互斥訊號

資源管理 最常用的獨佔共享資源和建立臨界區的方法有以下幾種: 關、開中斷 獨佔共享資源最簡單也是最快捷的方法就是關中斷和開中斷,當訪問共享資源的速度很快,以至於訪問共享資源所花的時間小於中斷的關閉時間時,可以使用關、開中斷方法。但是不推薦此方法

C++C++中變的聲明與定義的區別

分配 int -o sign 變量 range price ios urn 聲明(declaration):意味著告訴編譯器關於變量名稱、變量類型、變量大小、函數名稱、結構名稱、大小等等信息,並且在聲明階段不會給變量分配任何的內存。 定義(definition):定義就是

vxWorks互斥訊號示例

#include "vxWorks.h" #include "semLib.h" #include "taskLib.h" #include "logLib.h" #include "sysLib.h" #include "stdio.h" #define CON

FreeRTOS-互斥訊號

原文地址:http://blog.csdn.net/xukai871105/article/details/43456985 0.前言     在嵌入式作業系統中互斥型訊號量是任務間資源保護的重要手段。下面結合一個具體例子說明FreeRTOS中的互斥型訊號量如何使用。

pthread互斥訊號使用總結

----一年前寫的東西,重新抄錄以防遺忘。 glibc提供的pthread互斥訊號量可以用在程序內部,也可以用在程序間,可以在初始化時通過pthread_mutexattr_setpshared介面設定該訊號 量屬性,表示是程序內還是程序間。程序內的使用較為簡單,本文

UCOS2:對於訊號互斥訊號,事件標誌組

2.訊號量:    至於訊號量,和互斥訊號量是用區別的,簡單來說(個人理解,歡迎糾正)就是互斥訊號量再同一時刻,任務得到互斥訊號量量後是獨佔共享資源的,在他沒有釋放訊號量之前,任何其他任務都是不能訪問共享資源的。而訊號量的不同在於。訊號量可以設定一個值,允許最多又幾個任務同時去訪問共享資源。比如我給他設定一個

LinuxLinux程序訊號詳解

一、引入訊號概念訊號其實我們也見過,當我們在shell上寫出一個死迴圈退不出來的時候,只需要一個組合鍵,ctrl+c,就可以解決了,這就是一個訊號,但是真正的過程並不是那麼簡單的。1、當用戶按下這一對組合鍵時,這個鍵盤輸入會產生一個硬體中斷,如果CPU正在執行這個程序的程式碼