1. 程式人生 > >uCOS-II任務管理之改變任務優先順序 [轉載]

uCOS-II任務管理之改變任務優先順序 [轉載]

uCOS-II任務管理之改變任務的優先順序
在uCOS-II 裡,任務的優先順序也是是可以修改的。優先順序翻轉問題是可以通過改變任務優先順序解決。
那什麼是優先順序翻轉問題呢?
所謂優先順序翻轉問題(priority inversion)即當一個高優先順序任務通過訊號量機制訪問共享資源時,該訊號量已被一低優先順序任務佔有,而這個低優先順序任務在訪問共享資源時可能又被其它一些中等優先順序任務搶先,因此造成高優先順序任務被許多具有較低優先順序任務阻塞,實時性難以得到保證。
如果在訪問共享資源時,恰當地修改任務的優先順序就可以解決優先順序翻轉問題了。
但改變任務的優先順序也是很花時間的,如果不發生優先順序翻轉而提升了任務的優先順序,釋放資源後又改回原優先順序,則無形中浪費了許多CPU時。在mcu21專案中並沒有改變任務的優先順序。

改變任務的優先順序的操作也是很簡單的。主要是呼叫uCOS-II 系統函式OSTaskChangePrio (INT8U oldprio, INT8U newprio); ,它需要兩個引數,一個任務原來的優先順序,一個是任務改變後的優先順序。

下面深入地解剖下OSTaskChangePrio (INT8U oldprio, INT8U newprio)的實現過程:
系統函式OSTaskChangePrio()

INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)
{
    OS_TCB   *ptcb;
    OS_EVENT *pevent;
    INT8U     x;
    INT8U     y;
    INT8U     bitx;
    INT8U     bity;
 
    if ((oldprio >= OS_LOWEST_PRIO && oldprio != OS_PRIO_SELF)  ||        //(1)
         newprio >= OS_LOWEST_PRIO) {
        return (OS_PRIO_INVALID);
    }
    OS_ENTER_CRITICAL();
    if (OSTCBPrioTbl[newprio] != (OS_TCB *)0) {                           //(2)
        OS_EXIT_CRITICAL();
        return (OS_PRIO_EXIST);
    } else {
        OSTCBPrioTbl[newprio] = (OS_TCB *)1;                              //(3)
        OS_EXIT_CRITICAL();
        y    = newprio >> 3;                                              //(4)
        bity = OSMapTbl[y];
        x    = newprio & 0x07;
        bitx = OSMapTbl[x];
        OS_ENTER_CRITICAL();
        if (oldprio == OS_PRIO_SELF) {                                    //(5)
            oldprio = OSTCBCur->OSTCBPrio;
        }
        if ((ptcb = OSTCBPrioTbl[oldprio]) != (OS_TCB *)0) {              //(6)
            OSTCBPrioTbl[oldprio] = (OS_TCB *)0;                          //(7)
            if (OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX) {               //(8)
                if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {  //(9)
                    OSRdyGrp &= ~ptcb->OSTCBBitY;
                }
                OSRdyGrp    |= bity;                                      //(10)
                OSRdyTbl[y] |= bitx;
            } else {
                if ((pevent = ptcb->OSTCBEventPtr) != (OS_EVENT *)0) {    //(11)
                    if ((pevent->OSEventTbl[ptcb->OSTCBY] &=      
                         ~ptcb->OSTCBBitX) == 0) {
                        pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
                    }
                    pevent->OSEventGrp    |= bity;                        //(12)
                    pevent->OSEventTbl[y] |= bitx;
                }
            }
            OSTCBPrioTbl[newprio] = ptcb;                                 //(13)
            ptcb->OSTCBPrio       = newprio;                              //(14)
            ptcb->OSTCBY          = y;                                    //(15)
            ptcb->OSTCBX          = x;
            ptcb->OSTCBBitY       = bity;
            ptcb->OSTCBBitX       = bitx;
            OS_EXIT_CRITICAL();
            OSSched();                                                    //(16)
            return (OS_NO_ERR);
        } else {
            OSTCBPrioTbl[newprio] = (OS_TCB *)0;                          //(17)
            OS_EXIT_CRITICAL();
            return (OS_PRIO_ERR);
        }
    }
}

修改任務的優先順序,需要經過以下幾大步驟:
1、 首先,使用者不能改變空閒任務的優先順序(1),但使用者可以改變呼叫本函式的任務或者其它任務的優先順序。為了改變呼叫本函式的任務的優先順序,使用者可以指定該任務當前的優先順序或OS_PRIO_SELF,OSTaskChangePrio()會決定該任務的優先順序。使用者還必須指定任務的新(即想要的)優先順序。因為μC/OS-Ⅱ不允許多個任務具有相同的優先順序,所以OSTaskChangePrio()需要檢驗新優先順序是否是合法的(即不存在具有新優先順序的任務)(2)。如果新優先順序是合法的,μC/OS-Ⅱ通過將某些東西儲存到OSTCBPrioTbl[newprio]中保留這個優先順序(3)。如此就使得OSTaskChangePrio()可以重新允許中斷,因為此時其它任務已經不可能建立擁有該優先順序的任務,也不能通過指定相同的新優先順序來呼叫OSTaskChangePrio()。接下來OSTaskChangePrio()可以預先計算新優先順序任務的OS_TCB中的某些值(4)。而這些值用來將任務放入就緒表或從該表中移除(參看3.04,就緒表)。
2、 其次,OSTaskChangePrio()檢驗目前的任務是否想改變它的優先順序(5)。然後,OSTaskChangePrio()檢查想要改變優先順序的任務是否存在(6)。很明顯,如果要改變優先順序的任務就是當前任務,這個測試就會成功。但是,如果OSTaskChangePrio()想要改變優先順序的任務不存在,它必須將保留的新優先順序放回到優先順序表OSTCBPrioTbl[]中(17),並返回給呼叫者一個錯誤碼。
3、 再次,OSTaskChangePrio()可以通過插入NULL指標將指向當前任務OS_TCB的指標從優先順序表中移除了(7)。這就使得當前任務的舊的優先順序可以重新使用了。接著,我們檢驗一下OSTaskChangePrio()想要改變優先順序的任務是否就緒(8)。如果該任務處於就緒狀態,它必須在當前的優先順序下從就緒表中移除(9),然後在新的優先順序下插入到就緒表中(10)。這兒需要注意的是,OSTaskChangePrio()所用的是重新計算的值(4)將任務插入就緒表中的。
  如果任務已經就緒,它可能會正在等待一個訊號量、一封郵件或是一個訊息佇列。如果OSTCBEventPtr非空(不等於NULL)(8),OSTaskChangePrio()就會知道任務正在等待以上的某件事。如果任務在等待某一事件的發生,OSTaskChangePrio()必須將任務從事件控制塊(參看6.00,事件控制塊)的等待佇列(在舊的優先順序下)中移除。並在新的優先順序下將事件插入到等待佇列中(12)。任務也有可能正在等待延時的期滿(參看第五章-任務管理)或是被掛起(參看4.07,掛起任務,OSTaskSuspend())。在這些情況下,從(8)到(12)這幾行可以略過。

4、 最後,這也是最關鍵的一步,任務的優先順序就是這裡被修改的,其實簡單來說就是修改任務控制塊裡面的引數。在任務塊裡,OSTCBPrio、OSTCBY、OSTCBX、OSTCBBitY、OSTCBBitX這幾個變數的值標註了任務的優先順序,修改任務的優先順序就是修改這幾個變數的值。OSTaskChangePrio()將指向任務OS_TCB的指標存到OSTCBPrioTbl[]中(13)。新的優先順序被儲存在OS_TCB中(14),重新計算的值也被儲存在OS_TCB中(15)。OSTaskChangePrio()完成了關鍵性的步驟後,在新的優先順序高於舊的優先順序或新的優先順序高於呼叫本函式的任務的優先順序情況下,任務排程程式就會被呼叫(16)。