天天看點

從0到1教你寫UCOS-III 第十部分:就緒清單

       在 uC/OS-III 中,任務被建立後,任務的 TCB 會被放入就緒清單中,表示任務在就緒,随時可能被運作。就緒清單包含一個表示任務優先級的優先級表,一個存儲任務 TCB 的TCB雙向連結清單。

10.1 優先級表:

       優先級表在代碼層面上來看,就是一個數組,在檔案 os_prio.c(os_prio.c 第一次使用需要自行在檔案夾 uCOS-III\Source 中建立并添加到工程的 uC/OS-III Source 組) 的開頭定義,具體見代碼清單 10-1。

代碼清單 10-1 優先級表 OSPrioTbl[]定義

/* 定義優先級表,在 os.h 中用 extern 聲明 */
CPU_DATA OSPrioTbl[OS_PRIO_TBL_SIZE];         (1)      

       代碼清單 10-1(1):正如我們所說, 優先級表是一個數組,數組類型為 CPU_DATA,在 Cortex-M 内 核 晶片的 MCU 中 CPU_DATA 為 32 位 整型 。數組 的大 小由 宏OS_PRIO_TBL_SIZE 控制。 OS_PRIO_TBL_SIZE 的具體取值與 uC/OS-III 支援多少個優先級有關,支援的優先級越多,優先級表也就越大,需要的 RAM 空間也就越多。理論上uC/OS-III 支援無限的優先級,隻要 RAM 控制足夠。 宏 OS_PRIO_TBL_SIZE 在 os.h 檔案定義,具體實作見代碼清單 10-2。

代碼清單 10-2 OS_PRIO_TBL_SIZE 宏定義

(1)                                    (2)
#define OS_PRIO_TBL_SIZE ((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS) + 1u)      

        代碼清單 10-2(1): OS_CFG_PRIO_MAX 表示支援多少個優先級,在 os_cfg.h 中定義,本書設定為 32,即最大支援 32 個優先級。

        代碼清單 10-2(2): DEF_INT_CPU_NBR_BITS 定義 CPU 整型資料有多少位,本書适配的是基于 Cortex-M系列的 MCU,宏展開為 32位。

        是以,經過 OS_CFG_PRIO_MAX 和 DEF_INT_CPU_NBR_BITS 這兩個宏展開運算之後,可得出 OS_PRIO_TBL_SIZE 的值為 1,即優先級表隻需要一個成員即可表示 32 個優先級。 如果要支援 64 個優先級,即需要兩個成員,以此類推。 如果 MCU 的類型是 16 位、8 位或者 64位,隻需要把優先級表的資料類型 CPU_DATA改成相應的位數即可。

        那麼優先級表又是如何跟任務的優先級聯系在一起的? 具體的優先級表的示意圖見圖10-1。

從0到1教你寫UCOS-III 第十部分:就緒清單

       在圖 10-1 中,優先級表的成員是 32 位的,每個成員可以表示 32 個優先級。如果優先級超過 32 個,那麼優先級表的成員就要相應的增加。 以本書為例, CPU 的類型為 32 位,支援最大的優先級為 32 個,優先級表隻需要一個成員即可,即隻有 OSPrioTbl[0]。假如建立一個優先級為 Prio的任務,那麼就在 OSPrioTbl[0]的位[31-prio]置 1即可。如果 Prio等于3,那麼就将位 28 置 1。 OSPrioTbl[0]的位 31 表示的是優先級最高的任務,以此遞減,直到 OSPrioTbl[OS_PRIO_TBL_SIZE-1]]的位 0, OSPrioTbl[OS_PRIO_TBL_SIZE-1]]的位 0表示的是最低的優先級。

10.1.1 優先級表函數講解:

優先級表相關的函數在 os_prio.c 檔案中實作, 在 os.h 檔案中聲明, 函數彙總具體見表格 10-1。

表格 10-1 優先級表相關函數彙總

從0到1教你寫UCOS-III 第十部分:就緒清單

1、OS_PrioInit()函數:

      OS_PrioInit()函數用于初始化優先級表, 在 OSInit()函數中被調用, 具體實作見代碼清單 10-3。

代碼清單 10-3 OS_PrioInit()函數

/* 初始化優先級表 */
void OS_PrioInit( void )
{
CPU_DATA i;
/* 預設全部初始化為 0 */
for ( i=0u; i<OS_PRIO_TBL_SIZE; i++ ) {
OSPrioTbl[i] = (CPU_DATA)0;
}
}      

        本書中, 優先級表 OS_PrioTbl[]隻有一個成員, 即 OS_PRIO_TBL_SIZE 等于 1 經過代碼清單 10-3 初始化之後,具體示意圖見圖 10-2。

從0到1教你寫UCOS-III 第十部分:就緒清單

2、OS_PrioInsert()函數:

      OS_PrioInsert()函數用于置位優先級表中相應的位,會被 OSTaskCreate()函數調用,具體實作見代碼清單 10-4。

代碼清單 10-4 OS_PrioInsert()函數

/* 置位優先級表中相應的位 */
void OS_PrioInsert (OS_PRIO prio)
{
CPU_DATA bit;
CPU_DATA bit_nbr;
OS_PRIO ix;
/* 求模操作,擷取優先級表數組的下标索引 */
ix = prio / DEF_INT_CPU_NBR_BITS;                          (1)
/* 求餘操作,将優先級限制在 DEF_INT_CPU_NBR_BITS 之内 */
bit_nbr = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);    (2)
/* 擷取優先級在優先級表中對應的位的位置 */                    (3)
bit = 1u;
bit <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;
/* 将優先級在優先級表中對應的位置 1 */
OSPrioTbl[ix] |= bit;                                      (4)
}      

求模操作,擷取優先級表數組的下标索引。即定位 prio 這個優先級對應優先級表數組的哪個成員。 假設 prio等于 3, DEF_INT_CPU_NBR_BITS(用于表示 CPU一個整型數有多少位) 等于 32,那麼 ix 就等于 0,即對應 OSPrioTBL[0]。

       代碼清單 10-4(2) : 求餘操作,将優先級限制在 DEF_INT_CPU_NBR_BITS 之内,超過 DEF_INT_CPU_NBR_BITS 的優先級就肯定要增加優先級表的數組成員了。假設 prio等于 3, DEF_INT_CPU_NBR_BITS(用于表示 CPU 一個整型數有多少位) 等于 32,那麼bit_nbr就等于 3,但是這個還不是真正需要被置位的位。

       代碼清單 10-4(3) : 擷取優先級在優先級表中對應的位的位置。置位優先級對應的位是從高位開始的,不是從低位開始。位 31 對應的是優先級 0, 在 uC/OS-III 中,優先級數值越小,邏輯優先級就越高。 假設 prio 等于 3, DEF_INT_CPU_NBR_BITS(用于表示CPU一個整型數有多少位) 等于 32,那麼 bit就等于 28。

       代碼清單 10-4(4) : 将優先級在優先級表中對應的位置 1。 假設 prio 等于 3,DEF_INT_CPU_NBR_BITS(用于表示 CPU 一個整型數有多少位) 等于 32,那麼置位的就是 OSPrioTbl[0]的位 28。

        在優先級最大是 32, DEF_INT_CPU_NBR_BITS 等于 32 的情況下,如果分别建立了優先級 3、 5、 8 和 11 這四個任務,任務建立成功後,優先級表的設定情況是怎麼樣的?具體見圖 10-3。 有一點要注意的是,在 uC/OS-III 中,最高優先級和最低優先級是留給系統任務使用的,使用者任務不能使用。

從0到1教你寫UCOS-III 第十部分:就緒清單

3、OS_PrioRemove()函數:

        OS_PrioRemove()函數用于清除優先級表中相應的位,與 OS_PrioInsert()函數的作用剛好相反,具體實作見代碼清單 10-5

 ,有關代碼的講解參考代碼清單 10-4 即可,不同的是置位操作改成了清 0。

代碼清單 10-5 OS_PrioRemove()函數

/* 清除優先級表中相應的位 */
void OS_PrioRemove (OS_PRIO prio)
{
CPU_DATA bit;
CPU_DATA bit_nbr;
OS_PRIO ix;
/* 求模操作,擷取優先級表數組的下标索引 */
ix = prio / DEF_INT_CPU_NBR_BITS;
/* 求餘操作,将優先級限制在 DEF_INT_CPU_NBR_BITS 之内 */
bit_nbr = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);
/* 擷取優先級在優先級表中對應的位的位置 */
bit = 1u;
bit <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;
/* 将優先級在優先級表中對應的位清 0 */
OSPrioTbl[ix] &= ~bit;
}      

4、OS_PrioGetHighest()函數:

     OS_PrioGetHighest()函數用于從優先級表中查找最高的優先級,具體實作見代碼清單10-6。

代碼清單 10-6 OS_PrioGetHighest()函數

/* 擷取最高的優先級 */
OS_PRIO OS_PrioGetHighest (void)
{
CPU_DATA *p_tbl;
OS_PRIO prio;
prio = (OS_PRIO)0;
/* 擷取優先級表首位址 */
p_tbl = &OSPrioTbl[0];                          (1)
/* 找到數值不為 0 的數組成員 */                   (2)
while (*p_tbl == (CPU_DATA)0) {
prio += DEF_INT_CPU_NBR_BITS;
p_tbl++;
}
/* 找到優先級表中置位的最高的優先級 */
prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl);      (3)
return (prio);
}      

        代碼清單 10-6(1): 擷取優先級表的首位址,從頭開始搜尋整個優先級表,直到找到最高的優先級。

        代碼清單 10-6(2): 找到優先級表中數值不為 0的數組成員,隻要不為 0就表示該成員裡面至少有一個位是置位的。我們知道,在圖 10-4 的優先級表中,優先級按照從左到右,從上到下依次減小,左上角為最高的優先級,右下角為最低的優先級, 是以我們隻需要找到第一個不是 0 的優先級表成員即可。

        代碼清單 10-6(3): 确定好優先級表中第一個不為 0 的成員後,然後再找出該成員中第一個置 1 的位(從高位到低位開始找)就算找到最高優先級。 在一個變量中,按照從高位到低位的順序查找第一個置 1 的位的方法是通過計算前導 0 函數 CPU_CntLeadZeros()來實作的。 從高位開始找 1叫計算前導 0,從低位開始找 1叫計算後導 0。 如果分别建立了優先級 3、 5、 8和 11 這四個任務,任務建立成功後,優先級表的設定情況具體見圖 10-5。調用 CPU_CntLeadZeros()可以計算出 OSPrioTbl[0]第一個置 1的位前面有 3 個 0,那麼這個 3就是我們要查找的最高優先級,至于後面還有多少個位置 1 我們都不用管,隻需要找到第一個 1 即可。

從0到1教你寫UCOS-III 第十部分:就緒清單
從0到1教你寫UCOS-III 第十部分:就緒清單

         CPU_CntLeadZeros()函數可由彙編或者 C 來實作,如果使用的處理器支援前導零指令CLZ, 可由彙編來實作,加快指令運算,如果不支援則由 C 來實作。 在 uC/OS-III 中,這兩種實作方法均有提供代碼, 到底使用哪種方法由 CPU_CFG_LEAD_ZEROS_ASM_PRESEN這個宏來控制,定義了這個宏則使用彙編來實作,沒有定義則使用 C 來實作。

        Cortex-M 系列處理器自帶 CLZ 指令,是以 CPU_CntLeadZeros()函數預設由彙編編寫,具體在 cpu_a.asm 檔案實作,在 cpu.h 檔案聲明,具體見代碼清單 10-7。代碼清單 10-7 CPU_CntLeadZeros()函數實作與聲明

;*******************************************************************
; PUBLIC FUNCTIONS
;*******************************************************************
EXPORT CPU_CntLeadZeros
EXPORT CPU_CntTrailZeros
;*******************************************************************
; 計算前導 0 函數
;
; 描述 :
;
; 函數聲明 : CPU_DATA CPU_CntLeadZeros(CPU_DATA val);
;
;*******************************************************************
CPU_CntLeadZeros
CLZ R0, R0 ; Count leading zeros
BX LR
;*******************************************************************
; 計算後導 0 函數
;
; 描述 :
;
; 函數聲明 : CPU_DATA CPU_CntTrailZeros(CPU_DATA val);
;
;*******************************************************************
CPU_CntTrailZeros
RBIT R0, R0 ; Reverse bits
CLZ R0, R0 ; Count trailing zeros
BX LR
/*
*******************************************************************
* 函數聲明
* cpu.h 檔案
*******************************************************************
*/
#define CPU_CFG_LEAD_ZEROS_ASM_PRESEN
CPU_DATA CPU_CntLeadZeros (CPU_DATA val); /* 在 cpu_a.asm 定義 */
CPU_DATA CPU_CntTrailZeros(CPU_DATA val); /* 在 cpu_a.asm 定義 */      

        如果處理器不支援前導 0 指令, CPU_CntLeadZeros()函數就得由 C 編寫,具體在cpu_core.c 檔案實作,在 cpu.h 檔案聲明,具體見代碼清單 10-8。

代碼清單 10-8 由 C 實作的 CPU_CntLeadZeros()函數

#ifndef CPU_CFG_LEAD_ZEROS_ASM_PRESENT
CPU_DATA CPU_CntLeadZeros (CPU_DATA val)
{
CPU_DATA nbr_lead_zeros;
CPU_INT08U ix;
/* 檢查高 16 位 */
if (val > 0x0000FFFFu) {                                     (1)
/* 檢查 bits [31:24] : */
if (val > 0x00FFFFFFu) {                                     (2)
/* 擷取 bits [31:24]的值,并轉換成 8 位 */
ix = (CPU_INT08U)(val >> 24u);                               (3)
/* 查表找到優先級 */
nbr_lead_zeros=(CPU_DATA)(CPU_CntLeadZerosTbl[ix]+0u);       (4)
}
/* 檢查 bits [23:16] : */
else {
/* 擷取 bits [23:16]的值,并轉換成 8 位 */
ix = (CPU_INT08U)(val >> 16u);
/* 查表找到優先級 */
nbr_lead_zeros = (CPU_DATA )(CPU_CntLeadZerosTbl[ix] + 8u);
}
}
/* 檢查低 16 位 */
else {
/* 檢查 bits [15:08] : */
if (val > 0x000000FFu) {
/* 擷取 bits [15:08]的值,并轉換成 8 位 */
ix = (CPU_INT08U)(val >> 8u);
/* 查表找到優先級 */
nbr_lead_zeros = (CPU_DATA )(CPU_CntLeadZerosTbl[ix] + 16u);
}
/* 檢查 bits [07:00] : */
else {
/* 擷取 bits [15:08]的值,并轉換成 8 位 */
ix = (CPU_INT08U)(val >> 0u);
/* 查表找到優先級 */
nbr_lead_zeros = (CPU_DATA )(CPU_CntLeadZerosTbl[ix] + 24u);
}
}
/* 傳回優先級 */
return (nbr_lead_zeros);
}
#endif      

在 uC/OS-III 中,由 C 實作的 CPU_CntLeadZeros()函數支援 8 位、 16 位、 32 位和 64 位

的變量的前導 0 計算, 但最終的代碼實作都是分離成 8 位來計算。 這裡我們隻講解 32 位的,

其它幾種情況都類似。

代碼清單 10-8(1): 分離出高 16 位, else 則為低 16 位。

代碼清單 10-8(2): 分離出高 16 位的高 8 位, else 則為高 16 位的低 8 位。

代碼清單 10-8(3): 将高 16 位的高 8 位通過移位強制轉化為 8 位的變量,用于後面的查表操作。

代碼清單 10-8(4): 将 8 位的變量 ix 作為數組 CPU_CntLeadZerosTbl[]的索引,傳回索引對應的值,那麼該值就是 8 位變量 ix 對應的前導 0,然後再加上(24-右移的位數) 就等于優先級。 數組 CPU_CntLeadZerosTbl[]在 cpu_core.c 的開頭定義,具體見代碼清單 10-9。

代碼清單 10-9 CPU_CntLeadZerosTbl[]定義

#ifndef CPU_CFG_LEAD_ZEROS_ASM_PRESENT
static const CPU_INT08U CPU_CntLeadZerosTbl[256] = {/* 索引 */
8u,7u,6u,6u,5u,5u,5u,5u,4u,4u,4u,4u,4u,4u,4u,4u, /* 0x00 to 0x0F */
3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u, /* 0x10 to 0x1F */
2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u, /* 0x20 to 0x2F */
2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u, /* 0x30 to 0x3F */
1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u, /* 0x40 to 0x4F */
1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u, /* 0x50 to 0x5F */
1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u, /* 0x60 to 0x6F */
1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u, /* 0x70 to 0x7F */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0x80 to 0x8F */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0x90 to 0x9F */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0xA0 to 0xAF */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0xB0 to 0xBF */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0xC0 to 0xCF */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0xD0 to 0xDF */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u, /* 0xE0 to 0xEF */
0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u /* 0xF0 to 0xFF */
};
#endif      

       代碼清單 10-8 中,對一個 32 位的變量算前導 0 個數的時候都是分離成 8 位的變量來計算,然後将這個 8 位的變量作為數組 CPU_CntLeadZerosTbl[]的索引,索引下對應的值就是這個 8 位變量的前導 0 個數。一個 8 位的變量的取值範圍為 0~0XFF,這些值作為數組CPU_CntLeadZerosTbl[]的索引,每一個值的前導 0 個數都預先算出來作為該數組索引下的值。通過查 CPU_CntLeadZerosTbl[]這個表就可以很快的知道一個 8 位變量的前導 0 個數,根本不用計算,隻是浪費了定義 CPU_CntLeadZerosTbl[]這個表的一點點空間而已,在處理器記憶體很充足的情況下,則優先選擇這種空間換時間的方法。

10.2 就緒清單:

       準備好運作的任務的 TCB 都會被放到就緒清單中,系統可随時排程任務運作。 就緒清單在代碼的層面上看就是一個 OS_RDY_LIST 資料類型的數組 OSRdyList[],數組的大小由宏 OS_CFG_PRIO_MAX 決定,支援多少個優先級, OSRdyList[]就有多少個成員。任務的優先級與 OSRdyList[]的索引一一對應,比如優先級 3 的任務的 TCB 會被放到 OSRdyList[3]中。 OSRdyList[]是一個在 os.h 檔案中定義的全局變量,具體見代碼清單 10-10。代碼清單 10-10 OSRdyList[]數組定義

/* 就緒清單定義 */
OS_EXT OS_RDY_LIST OSRdyList[OS_CFG_PRIO_MAX];      

        代碼清單 10-10 中的資料類型 OS_RDY_LIST 在 os.h 中定義,專用于就緒清單,具體實作見代碼清單 10-11。

        代碼清單 10-11 OS_RDY_LIST 定義

typedef struct os_rdy_list OS_RDY_LIST; (1)
struct os_rdy_list {
OS_TCB *HeadPtr; (2)
OS_TCB *TailPtr;
OS_OBJ_QTY NbrEntries; (3)
};      

       代碼清單 10-11(1):在 uC/OS-III 中,核心對象的資料類型都會用大寫字母重新定義。

       代碼清單 10-11(2): OSRdyList[]的成員與任務的優先級一一對應,同一個優先級的多個任務會以雙向連結清單的形式存在 OSRdyList[]同一個索引下,那麼 HeadPtr 就用于指向連結清單的頭節點, TailPtr 用于指向連結清單的尾節點,該優先級下的索引成員的位址則稱為該優先級下雙向連結清單的根節點,知道根節點的位址就可以查找到該連結清單下的每一個節點。

       代碼清單 10-11(3): NbrEntries 表示 OSRdyList[]同一個索引下有多少個任務。一個空的就緒清單, OSRdyList[]索引下的 HeadPtr、 TailPtr 和 NbrEntrie 都會被初始化為 0,具體見圖 10-6。

從0到1教你寫UCOS-III 第十部分:就緒清單

        就緒清單相關的所有函數都在 os_core.c 實作,這些函數都是以“OS_”開頭,表示是OS 的内部函數,使用者不能調用,這些函數的彙總具體見表格 10-2。

表格 10-2 就緒清單相關函數彙總

從0到1教你寫UCOS-III 第十部分:就緒清單

10.2.1 就緒清單函數講解:

       在實作就緒清單相關函數之前,我們需要在結構體 os_tcb 中添加 Prio、 NextPtr 和PrevPtr 這三個成員,然後在 os.h 中定義兩個全局變量 OSPrioCur 和 OSPrioHighRdy, 具體定義見代碼清單 10-12。接下來要實作的就緒清單相關的函數會用到幾個變量。

代碼清單 10-12 就緒清單函數需要用到的變量定義

struct os_tcb {
CPU_STK *StkPtr;
CPU_STK_SIZE StkSize;
/* 任務延時周期個數 */
OS_TICK TaskDelayTicks;
/* 任務優先級 */
OS_PRIO Prio;
/* 就緒清單雙向連結清單的下一個指針 */
OS_TCB *NextPtr;
/* 就緒清單雙向連結清單的前一個指針 */
OS_TCB *PrevPtr;
};
/* 在 os.h 中定義 */
OS_EXT OS_PRIO OSPrioCur; /* 目前優先級 */
OS_EXT OS_PRIO OSPrioHighRdy; /* 最高優先級 */      

1. OS_RdyListInit()函數:

       OS_RdyListInit()用于将就緒清單 OSRdyList[]初始化為空,初始化完畢之後具體示意圖見圖 10-6,具體實作見代碼清單 10-13。

代碼清單 10-13 OS_RdyListInit()函數

void OS_RdyListInit(void)
{
OS_PRIO i;
OS_RDY_LIST *p_rdy_list;
/* 循環初始化,所有成員都初始化為 0 */
for ( i=0u; i<OS_CFG_PRIO_MAX; i++ ) {
p_rdy_list = &OSRdyList[i];
p_rdy_list->NbrEntries = (OS_OBJ_QTY)0;
p_rdy_list->HeadPtr = (OS_TCB *)0;
p_rdy_list->TailPtr = (OS_TCB *)0;
}
}      

2. OS_RdyListInsertHead()函數:

       OS_RdyListInsertHead()用于在連結清單頭部插入一個 TCB 節點,插入的時候分兩種情況,第一種是連結清單是空連結清單,第二種是連結清單中已有節點,具體示意圖見圖 10-7,具體的代碼實

現見代碼清單 10-14,閱讀代碼的時候最好配套示意圖來了解。

從0到1教你寫UCOS-III 第十部分:就緒清單

代碼清單 10-14 OS_RdyListInsertHead()函數

void OS_RdyListInsertHead (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb2;
/* 擷取連結清單根部 */
p_rdy_list = &OSRdyList[p_tcb->Prio];
/* CASE 0: 連結清單是空連結清單 */
if (p_rdy_list->NbrEntries == (OS_OBJ_QTY)0) {
p_rdy_list->NbrEntries = (OS_OBJ_QTY)1;
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb->PrevPtr = (OS_TCB *)0;
p_rdy_list->HeadPtr = p_tcb;
p_rdy_list->TailPtr = p_tcb;
}
/* CASE 1: 連結清單已有節點 */
else {
p_rdy_list->NbrEntries++;
p_tcb->NextPtr = p_rdy_list->HeadPt
p_tcb->PrevPtr = (OS_TCB *)0;
p_tcb2 = p_rdy_list->HeadPt
p_tcb2->PrevPtr = p_tcb;
p_rdy_list->HeadPtr = p_tcb;
}
}      

3. OS_RdyListInsertTail()函數:

       OS_RdyListInsertTail()用于在連結清單尾部插入一個 TCB 節點,插入的時候分兩種情況,第一種是連結清單是空連結清單,第二種是連結清單中已有節點,具體示意圖見圖 10-8,具體的代碼實作見,閱讀代碼的時候最好配套示意圖來了解。

從0到1教你寫UCOS-III 第十部分:就緒清單

代碼清單 10-15 OS_RdyListInsertTail()函數

void OS_RdyListInsertTail (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb2;
/* 擷取連結清單根部 */
p_rdy_list = &OSRdyList[p_tcb->Prio];
/* CASE 0: 連結清單是空連結清單 */
if (p_rdy_list->NbrEntries == (OS_OBJ_QTY)0) {
p_rdy_list->NbrEntries = (OS_OBJ_QTY)1;
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb->PrevPtr = (OS_TCB *)0;
p_rdy_list->HeadPtr = p_tcb;
p_rdy_list->TailPtr = p_tcb;
}
/* CASE 1: 連結清單已有節點 */
else {
p_rdy_list->NbrEntries++;
p_tcb->NextPtr = (OS_TCB *)0;
p_tcb2 = p_rdy_list->TailPtr;
p_tcb->PrevPtr = p_tcb2;
p_tcb2->NextPtr = p_tcb;
p_rdy_list->TailPtr = p_tcb;
}
}      

4. OS_RdyListInsert()函數:

       OS_RdyListInsert()用于将任務的 TCB 插入到就緒清單,插入的時候分成兩步,第一步是根據優先級将優先級表中的相應位置位,這個調用 OS_PrioInsert()函數來實作,第二步是根據優先級将任務的 TCB 放到 OSRdyList[優先級]中,如果優先級等于目前的優先級則插入到連結清單的尾部,否則插入到連結清單的頭部,具體實作見代碼清單 10-16。代碼清單 10-16 OS_RdyListInsert()函數

/* 在就緒連結清單中插入一個 TCB */
void OS_RdyListInsert (OS_TCB *p_tcb)
{
/* 将優先級插入到優先級表 */
OS_PrioInsert(p_tcb->Prio);
if (p_tcb->Prio == OSPrioCur) {
/* 如果是目前優先級則插入到連結清單尾部 */
OS_RdyListInsertTail(p_tcb);
} else {
/* 否則插入到連結清單頭部 */
OS_RdyListInsertHead(p_tcb);
}
}      

5. OS_RdyListMoveHeadToTail()函數:

       OS_RdyListMoveHeadToTail()函數用于将節點從連結清單頭部移動到尾部,移動的時候分四種情況,第一種是連結清單為空,無事可做;第二種是連結清單隻有一個節點,也是無事可做;第三種是連結清單隻有兩個節點;第四種是連結清單有兩個以上節點,具體示意圖見圖 10-9,具體

代碼實作見代碼清單 10-17,閱讀代碼的時候最好配套示意圖來了解。

從0到1教你寫UCOS-III 第十部分:就緒清單

代碼清單 10-17 OS_RdyListMoveHeadToTail()函數

void OS_RdyListMoveHeadToTail (OS_RDY_LIST *p_rdy_list)
{
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
OS_TCB *p_tcb3;
switch (p_rdy_list->NbrEntries) {
case 0:
case 1:
break;
case 2:
p_tcb1 = p_rdy_list->HeadPtr;
p_tcb2 = p_rdy_list->TailPtr;
p_tcb1->PrevPtr = p_tcb2;
p_tcb1->NextPtr = (OS_TCB *)0;
p_tcb2->PrevPtr = (OS_TCB *)0;
p_tcb2->NextPtr = p_tcb1;
p_rdy_list->HeadPtr = p_tcb2;
p_rdy_list->TailPtr = p_tcb1;
break;
default:
p_tcb1 = p_rdy_list->HeadPtr;
p_tcb2 = p_rdy_list->TailPtr;
p_tcb3 = p_tcb1->NextPtr;
p_tcb3->PrevPtr = (OS_TCB *)0;
p_tcb1->NextPtr = (OS_TCB *)0;
p_tcb1->PrevPtr = p_tcb2;
p_tcb2->NextPtr = p_tcb1;
p_rdy_list->HeadPtr = p_tcb3;
p_rdy_list->TailPtr = p_tcb1;
break;
}
}      

6. OS_RdyListRemove()函數:

        OS_RdyListRemove()函數用于從連結清單中移除一個節點,移除的時候分為三種情況,第一種是連結清單為空,無事可做;第二種是連結清單隻有一個節點;第三種是連結清單有兩個以上節點,具體示意圖見圖 10-10,具體代碼實作見,閱讀代碼的時候最好配套示意圖來了解。

從0到1教你寫UCOS-III 第十部分:就緒清單
void OS_RdyListRemove (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
p_rdy_list = &OSRdyList[p_tcb->Prio];
/* 儲存要删除的 TCB 節點的前一個和後一個節點 */
p_tcb1 = p_tcb->PrevPtr;
p_tcb2 = p_tcb->NextPtr;
/* 要移除的 TCB 節點是連結清單中的第一個節點 */
if (p_tcb1 == (OS_TCB *)0) {
/* 且該連結清單中隻有一個節點 */
if (p_tcb2 == (OS_TCB *)0) {
/* 根節點全部初始化為 0 */
p_rdy_list->NbrEntries = (OS_OBJ_QTY)0;
p_rdy_list->HeadPtr = (OS_TCB *)0;
p_rdy_list->TailPtr = (OS_TCB *)0;
/* 清除在優先級表中相應的位 */
OS_PrioRemove(p_tcb->Prio);
}
/* 該連結清單中不止一個節點 */
else {
/* 節點減 1 */
p_rdy_list->NbrEntries--;
p_tcb2->PrevPtr = (OS_TCB *)0;
p_rdy_list->HeadPtr = p_tcb2;
}
}
/* 要移除的 TCB 節點不是連結清單中的第一個節點 */
else {
p_rdy_list->NbrEntries--;
p_tcb1->NextPtr = p_tcb2;
/* 如果要删除的節點的下一個節點是 0,即要删除的節點是最後一個節點 */
if (p_tcb2 == (OS_TCB *)0) {
p_rdy_list->TailPtr = p_tcb1;
} else {
p_tcb2->PrevPtr = p_tcb1;
}
}
/* 複位從就緒清單中删除的 TCB 的 PrevPtr 和 NextPtr 這兩個指針 */
p_tcb->PrevPtr = (OS_TCB *)0;
p_tcb->NextPtr = (OS_TCB *)0;
}      

10.3 main 函數:

10.4 實驗現象: