SylixOS線程調度淺析

  1. 概念

    在SylixOS中,在就緒線程和運行線程之間還存在候選運行線程。線程就緒後會被放置到就緒表中,而最須要運行(優先級最高)的線程會被放置到候選表,正常狀況下CPU當前任務結束後,會運行候選表裏的線程。函數

  2. 就緒表

    就緒表存放了SylixOS中除了"候選表中的線程"外,全部就緒沒運行的線程。spa

  3. 候選表

    每個CPU的結構體裏面都有一個候選運行表,每個候選表裏最多有一個候選運行線程。候選運行表結構如程序清單 11所示。線程

    程序清單 11 候選運行表結構code

    /*************************************************************************************************
      候選運行表結構
    *************************************************************************************************/
    #ifdef __SYLIXOS_KERNEL
    
    typedef struct {
        volatile PLW_CLASS_TCB  CAND_ptcbCand;                        /*  候選運行線程                */
        volatile BOOL           CAND_bNeedRotate;                     /*  可能產生了優先級卷繞         */
    } LW_CLASS_CAND;
    typedef LW_CLASS_CAND      *PLW_CLASS_CAND;
  4. 優先級卷繞

    如圖 11所示,優先級卷繞是CPU結構體裏面的候選表裏的標誌位CAND_bNeedRotate。it

    圖 11 候選運行表基本操做io

  5. 優先級卷繞的產生

    當候選表不爲空時,有一個優先級高於"候選表中線程"的線程就緒時,會產生優先級卷繞並將CPU的優先級卷繞標誌位設置爲1。具體流程如圖 12所示。class

    圖 12 優先級卷繞產生的流程object

     

  6. 線程調度

    系統主要在退出內核和退出中斷時嘗試進行線程調度。嘗試進行線程調度的主要函數有:__kernelExit函數、__kernelExitIrq函數、__kernelSched函數和__kernelSchedInt函數。這裏以__kernelExit函數爲例介紹線程調度。date

  7. 線程調度流程

    SylixOS線程調度流程如圖 21所示。map

    圖 21 線程調度流程

  8. 嘗試調度

    嘗試調度,檢查當前執行線程可否調度。(中斷中或者在內核中執行, 不容許調度;當前線程就緒且被鎖定, 不容許調度。)如程序清單 21所示。

    1. 若是當前執行線程不能調度,繼續運行當前線程,不產生調度。

    2. 若是當前執行線程能調度,繼續判斷是否產生了優先級卷繞。

    程序清單 21 _SchedGetCand函數

    PLW_CLASS_TCB  _SchedGetCand (PLW_CLASS_CPU  pcpuCur, ULONG  ulCurMaxLock)
    {
        if (!__COULD_SCHED(pcpuCur, ulCurMaxLock)) {                        /*  當前執行線程不能調度    */
            return  (pcpuCur->CPU_ptcbTCBCur);
            
        } else {                                                            /*  能夠執行線程切換       */
            if (LW_CAND_ROT(pcpuCur)) {                                     /*  判斷是否產生優先級卷繞  */
                _CandTableUpdate(pcpuCur);
            }
            return  (LW_CAND_TCB(pcpuCur));
        }
    }

     

  9. 優先級卷繞處理

    優先級卷繞的處理,如程序清單 22所示。

    第一步,若是產生了優先級卷繞,判斷候選表是否爲空。

    第二步,若是候選表不爲空,判斷就緒線程是否存在更加須要運行的線程。

    第三步,若是存在更加須要運行的線程,清空候選表(被清空的線程,會被插到對應就緒表的頭部,下次優先調用),從新選擇一個更須要運行的線程放入候選表,進行線程上下文切換,執行候選表中線程。

    程序清單 22 優先級就卷繞處理函數

    VOID _CandTableUpdate (PLW_CLASS_CPU   pcpu)
    {
                 UINT8              ucPriority;
        REGISTER PLW_CLASS_TCB      ptcbCand;
                 PLW_CLASS_PCBBMAP  ppcbbmap;
                 BOOL               bNeedRotate = LW_FALSE;
    
        if (!LW_CPU_IS_ACTIVE(pcpu)) {                                      /*  CPU 必須爲激活狀態   */
            return;
        }
        
        ptcbCand = LW_CAND_TCB(pcpu);
        if (ptcbCand == LW_NULL) {                                          /*  當前沒有候選線程      */
            _CandTableFill(pcpu);
            goto    __update_done;
        }
        
        ppcbbmap = _SchedSeekPriority(pcpu, &ucPriority);                   /*  當前就緒表中最高優先級 */
        if (ppcbbmap == LW_NULL) {
            LW_CAND_ROT(pcpu) = LW_FALSE;                                   /*  清除優先級卷繞標誌    */
            return;
        }
        
        if (ptcbCand->TCB_usSchedCounter == 0) {                            /*  已經沒有時間片了      */
            if (LW_PRIO_IS_HIGH_OR_EQU(ucPriority, 
                                       ptcbCand->TCB_ucPriority)) {         /*  是否須要輪轉         */
                bNeedRotate = LW_TRUE;
            }
        } else {
            if (LW_PRIO_IS_HIGH(ucPriority, 
                                ptcbCand->TCB_ucPriority)) {
                bNeedRotate = LW_TRUE;
            }
        }
        
        if (bNeedRotate) {                                                  /*  存在更須要運行的線程  */
            _CandTableEmpty(pcpu);                                          /*  清空候選表          */
            _CandTableResel(pcpu, ppcbbmap, ucPriority);                    /*  從新選擇任務執行     */
        }
        
    __update_done:
        LW_CAND_ROT(pcpu) = LW_FALSE;                                       /*  清除優先級卷繞標誌    */
    }
相關文章
相關標籤/搜索