uCOS-II中就緒表到最高優先級的查表表格解讀
接觸過uCOS-II的都應該知道,任務調度中的就緒表由兩部分組成:OSRdyGrp和OSRdyTbl。在需要進行任務切換時,通過:
y=OSUnMapTbl[OSRdyGrp]; // 獲得優先級別的D5,D4,D3位, 先找出是哪一組
x=OSUnMapTbl[OSRdyTbl[y]]; //獲得優先級的D2,D1,D0位, 再從哪一組中找出是哪一位
prio = (y<<3) + x; // 獲得就緒任務的優先級別
即可以查到當前就緒態的任務中的最高優先級任務。
在此,便有一個疑問:為什么通過查找OSUnMapTbl中的數據,便能間接得到當前就緒態的任務中的最高優先級呢?
首先,來看看OSRdyGrp和OSRdyTbl。
接着,再看看OSUnMapTbl的數據內容:
INT8U const OSUnMapTbl[256]={
0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
.....
};
表格中的數據是如何得到的呢?其實這些數據就是0~255數據字節從低位到高位中(即從左到右)第一個被置1的位的位置。具體過程如下:(大家可以對照源碼中OSUnMapTbl 數組)
…OSUnMapTbl[0] = 0
…OSUnMapTbl[1] = 0
…OSUnMapTbl[2] = 1
…OSUnMapTbl[3] = 0
…OSUnMapTbl[4] = 2
…OSUnMapTbl[5] = 0
…OSUnMapTbl[6] = 1
…OSUnMapTbl[7] = 0
…OSUnMapTbl[8] = 3
……
…OSUnMapTbl[15] = 0
…OSUnMapTbl[16] = 4
…OSUnMapTbl[31] = 5
…OSUnMapTbl[47] = 4
…OSUnMapTbl[63] = 6
……
…OSUnMapTbl[240] = 4
……
…OSUnMapTbl[255] = 0
Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 0 0
0x00 沒有被置1的位,故位置為0
0x01 首個被置1的位置為bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 0 1
0x02 首個被置1的位置為bit1,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 1 0
0x03 首個被置1的位置為bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 0 1 1
0x04 首個被置1的位置為bit2,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 0 0
0x05 首個被置1的位置為bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 0 1
0x06 首個被置1的位置為bit1,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 1 0
0x07 首個被置1的位置為bit0,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 1 1 1
0x08 首個被置1的位置為bit3,Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 1 0 0 0
① 首先將就緒任務組OSRdyGrp 用來在OSUnMapTbl[]中進行查表,找出OSRdyGrp 的位中首次置1位置,確定出當前就緒表中最高優先級是哪一組任務,即優先級的D5,D4,D3位。
② OSUnMapTbl[OSRdyGrp]得到的當前就緒表中最高優先級所在的組屬性值y ,再通過y 從就緒表中取出當前最高就緒任務所在就緒組的值OSRdyTbl[y]。
③ 再將OSRdyTbl[y]來在OSUnMapTbl[]中進行查表,找出OSRdyTbl[y]的位中首次置1的位置,確定出當前就緒任務組中最高優先級的到底是哪一個任務x ,即優先級的D2,D1,D0位。
④ 如此就得出了當前就緒表中最高優先級的標識prio = (y<<3) + x 。
UCOS II怎么查找當前就緒表中,優先級最高的任務
uCOS-II為了保證CPU總是執行優先級最高的任務,每當任務狀態發生變化時,就需要判斷當前任務是否為最高優先級,不是的話就需要進行上下文切換。如何在需要進行任務優先級比較時,快速的將就緒態任務中優先級最高的讀出。一種最簡單的數據結構就是
定義一個unsigned int類型的變量。變量長度為4字節共32位,如果用1代表任務就緒,用0代表任務未就緒,用相應的位來代表任務優先級,那么這樣總共能定義32個任務。

如上圖所示,第一行代表該位的取值,第二行代表位,那么總共定義了32個任務,優先級分別為0-31,其中優先級為0,、29、31的任務處於就緒狀態。這個說白了就是定義了一個長度為32的bool型數組,下標代表任務的優先級,該下標對應的值便為任務的狀態。
如果要讀出就緒表中處於就緒狀態並且優先級最高的任務,那么執行這條語句:
for(i=0;i<32&&(!(num&(0x01<<i)));i++);
其中num為定義的unsigned int 類型的變量,執行完這條語句之后,i的值就是就緒任務中優先級最高的任務所對應的優先級。這樣做確實簡單明了,但是定義的任務偏少,而且讀出一個特定任務優先級所需要的時間是不定的,就是說上面讀出優先級的那條語句的
執行次數是不定的,這顯然不能滿足實時性系統的要求。
所以就有了uCOS-II的神來之筆,unsigned int類型的變量可以看做是長度為32的bool型數組,如果將這個數組從一維定義到二維,那么定義的任務數量將翻倍增長。如果這個二維數組的列數為8,那么8位二進制數剛好可以用unsigned char來表示,那么定義一個一維
的unsigned char數組就可以表示這種二維的bool型數組,uCOS-II總共可以管理64個任務,那么unsigned char數組的長度為8,便可以定義出64個任務,剛好滿足要求。定義完之后的數據結構如下。

假設這個數組為data[],那么在上圖中,data[0]=0xA6,data[6]=0X37,在第一行中,優先級為1、2、5、7的任務處於就緒狀態,如何快速的讀出處於就緒狀態且優先級最高的任務,最笨的辦法就是遍歷這個數組,依次取出數組中的每個元素,每取出一個元素,
讀這個元素各個位的值,從低到高,直到找出第一個位為1的那一位。用元素的下標乘8再加上該位的值,就是優先級最高的任務。這種做法顯然效率很低,而且執行這個算法的所需要的時間不定。
這個算法效率低下的原因就是把時間浪費在了讀取那些未就緒任務的狀態,假設就緒態任務中優先級最高為103,那么依次讀取前102個任務的狀態值浪費了大量的時間,如果能想辦法跳過一些前面未就緒的任務,那么算法的效率就大大提升。
如果將所有的任務分組,每八個為一組,總共八組,定義一個unsigned char類型的變量,假設為grp,用這個變量的每一位代表一個任務組。只要這個任務組中八個任務其中有一個處於就緒態,那么就將這個grp變量相應的位置1,如果所有的任務都未就緒,那么
就將這個變量相應的位置0。這樣就達到了前面所說跳過一些未就緒任務的想法,在遍歷數組查找優先級時,從低到高首先去讀grp每一位的值,如果發現某一位值為0,就代表這個組沒有就緒態的任務,就跳過這個組。這樣遍歷data數組時,步長從1變為了8。
解決了前面的問題后,這個算法效率還是有些低,原因在於遍歷。假設grp的值為0XA0(1010 0000),遍歷的結果是優先級最高的就緒態任務在第五組,for(i=0;i<32&&(!(num&(0x01<<i)));i++);
上面這句代碼執行了五次,時間浪費在這里了。如果將代碼執行的次數降低到最小,並且是確定的,那么效率將會進一步提升。
任務就緒表
每個任務的就緒態標志放入在就緒表中,就緒表中有兩個變量OSRdyGrp和OSRdyTb1[]。
在OSRdyGrp中,任務按優先級分組,8個任務為一組。OSRdyGrp中的每一位表示8組任務中每一組中是否有進入就緒態的任務。任務進入就緒態時,就緒表OSRdyTbl[]中相應元素的相應位也置位。

n 假設優先級為12的任務進入就緒態,12=1100b,則OSRdyTbl[1]的第4位置1,且OSRdyGrp的第1位置位,相應的數學表達式為:
OSRdyTbl[1]|=0x10;
OSRdyGrp|=0x02;
n 而優先級為21的任務就緒態,21=10101b,則OSRdyTbl[2]的第5位置1,且OSRdyGrp的第2位置1 ,相應的數學表達式為:
OSRdyTbl[1]|=0x20;
OSRdyGrp|=0x04;


轉自:http://blog.sina.com.cn/s/blog_446cfdc60100ns3f.html
