在嵌入式操做系統複習中,咱們瞭解了μC/OS-II的相關基礎知識,在任務調度這一節,咱們提到了
優先級位圖算法
,本文詳細介紹該算法的原理和實現。 說明: 本文參考了這篇文章,加入了一些本身的理解,若有侵權,請聯繫刪除:原文連接html
一、μC/OS-II任務優先級相關簡介:μC/OS-II中共有64個優先級(0~63級,數字越小優先級越高)。由於是實時系統,因此對應每一個任務就分配一個優先級。web
二、2進制和10進制轉換基礎 這裏先介紹一個數學知識,二進制如何變爲十進制,好比十進制26,其8位二進制表示爲:00 011 010
。當十進制爲0~63時,前兩位無做用,因此只看後6位——011 010
.怎麼計算成十進制呢?很簡單:把這個十進制數,分爲兩個部分,高三位和低三位,這個十進制數的大小就等於高三位的十進制8+低三位的十進制數(其實就是二進制轉八進制再轉十進制)。高三位的011=3
,低三位的010=2
,因此26=3x8+2=(011)<<3+(010).即將高三位左移三位
就是8再加上低三位。下面要介紹的算法也是這個數學方法。算法
三、優先級任務調度過程數組
就緒任務
)進行標記。並經過標記來查找當中任務中優先級最高的任務
優先級建立 μC/OS-II中建立任務的函數原型: INT8U OSTASKCeate(void (*task)(void *pd),void *pdata,os_stk *ptos,INT8U prio)
,從這個函數能夠看出,最後一個參數就是優先級,因此結論是,在建立任務的同時就要肯定任務的優先級,而且是該優先級是8位的(0~2^8-1),這裏也能夠看出爲何會有64個優先級。數據結構
由於用戶能夠指定任務的優先級,但實時操做系統最大的好處就是高優先級的任務能夠搶佔低優先級的任務,那怎麼實現的呢?固然是經過優先級實現。 既然用戶在調用系統函數建立任務的同時指定了任務的優先級,一旦建立了任務,該任務就會當即成爲就緒狀態,操做系統就會將該任務的優先級
標誌位置位1
,至關於作個記號,操做系統內心就會想,哦,這個優先級的任務已經就緒了,一樣建立了其餘的任務,操做系統都會在某個地方作好標記代表對應優先級的任務已經就緒,而後在調度函數的調度下進行調度,那麼在哪一個地方作個標記呢?既然是實時操做系統,操做系統用什麼算法去查找優先級最高的任務呢?編輯器
任務優先級的標定 什麼是優先級的標定:就是操做系統要知道哪一個任務已經就緒了,而後就在這些就緒了的任務裏面切換調度。因此第一步就是要知道哪些任務就緒了,而後就能夠操做了。 這裏要先介紹兩個數據結構,:OSRdyGrp、OSRdyTbl[]
,這兩個變量協同完成優先級的標定。 OSRdyGrp
:優先級就緒組 這是一個8位的變量。每個變量對應於OSRdyTbl[]中的一行(其實是一個元素,但也能夠理解爲一行)。 OSRdyTbl[]
:優先級就緒表 這是一個數組,有8個成員,每一個成員都是8位的變量,因此就能夠構成了8*8的矩陣了。因此64個優先級都是標定在這個數組中的。 函數
從上圖能夠明顯看出,這個圖有64個空格(64個位),每一個空格對應一個數字,該數字就是優先級的標號,可是這個是人爲的標上的,實際上這是64個空格,如今要作的事情就是將就緒任務的優先級相對應的標號置1,表示這個優先級任務就緒了,好比剛建立了一個任務,它的優先級是7,因此往表格中數字爲7的空格寫入1就代表該優先級的任務就緒了,能夠被調度了。另外當全部須要建立任務都建立完畢後,各個就緒任務的相應數字空格都會置1,代表這些任務都就緒了,好比,如今建立了5個任務,優先級分別爲4,7,9,10,24.因此在建立完這些任務後,這個優先級就緒表中的相對應的數字空格都會被置1,要特別注意上圖的優先級順序,0的優先級最高,63的優先級最低。url
那到底怎麼往空格里置1的呢? 這裏就要分析這個優先級就緒表了
:spa
1.這個表的數據結構是數組,也就是說,這個數組有8個成員,每一個成員都是8位的變量。 2.經過二級查找實現對就緒任務的標定的。這裏能夠理解爲一個矩陣,找某個數,確定是先找行,再找列。從而找到這個元素位置。思想就是這個。 怎麼找行呢?這裏的一個變量OSRdyGrp,是一個8位的變量。每一位對應上圖的一行,當某一位置1時,就代表就緒表對應的行有就緒任務,而後再查找列,就能夠找到哪一個任務就緒了。如今舉個列子來講明:操作系統
建立一個任務,它的優先級爲 prio=11(這是用戶指定的),此優先級用二進制表示(8位):最前面兩位無用處,前面已說過 00 001 011
。那麼怎麼在對應的OSRdyTbl[]優先級就緒表中進行標定呢?
對上面這個數來講 001 =1說明是第1行(數組從0行開始),011=3說明在第3個位置要寫入1,合在一塊兒就是第一行的第三個位置寫入1
,這樣就完成了對應數字優先級標號的標記。
那從上面的思路來看,咱們只要知道數組中的第幾行和第幾列的值就能夠了,因此又引進了一個映射數組
: OSMapTbl[]
,其具體內容以下。下標0對應的就是0位爲1,下標1對應的就是1位爲1,而後把這個數賦值給OSRdyGrp優先級就緒組。則OSRdyGrp哪一個位爲1則說明就是就緒表哪一個行有就緒任務了。這樣作的好處就是快。這也就是這個數組就是個映射數組,方便操做而已。
下標 | 二進制值 |
---|---|
0 | 00000001 |
1 | 00000010 |
2 | 00000100 |
3 | 00001000 |
4 | 00010000 |
5 | 00100000 |
6 | 01000000 |
7 | 10000000 |
至此,以上涉及3個數據結構了,如今來總結一下: 1.OSRdyGrp
優先級就緒組:第幾位被置1,就說明就緒表中第幾行有就緒任務。好比OSRdyGrp=0000 0001
。說明OSRdyTbl[0]
行有就緒任務。具體是這行的哪一個列還要根據低三位
的值來決定. 2.OSRdyTbl[]
優先級就緒表:行+列就能標定就緒任務的優先級. 3. OSMapTbl[]
優先級映射表:用來方便生成第幾行,第幾列的一個轉換.
下面來看ucos中的源碼怎麼實現的: 任務就緒源碼以下:
OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];
代碼解釋:prio>>3是獲取優先級的高3位
,prio&0x07是獲取優先級的低3位
。而後在經過OSMapTbl的映射分別得到了就緒表中的行和就緒表中的列, 而後查詢就緒算法:
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
舉個例子: 建立一個任務,且prio=11=001 011
的狀況分析: 高3位
:001=1
經過OSMapTbl
映射後,OSRdyGrp=0000 0010,便是就緒表中第1行有任務就緒。 低3位
:011=3,經過OSMapTbl
映射後
//低三位的映射
OSMapTbl[prio&0x07] = OSMapTbl[3] = 0000 1000;
OSRdyTbl[prio>>3] = OSRdyTbl[1] = 0000 1000;
經過這句代碼就往就緒表第一行(從OSRdyTbl[1]看到)第3個位置(從右往左看0000 1000,第一個爲1的位,0開始)寫入1,代表該任務就緒了。
這樣就完成了單個任務優先級的標定。
多任務優先級設定 這裏引入一個表格:優先級斷定表OSUnMapTbl[]
,這個表的做用是爲了節省時間,這樣查表的話,耗的時間是必定的,很好的知足了實時性。下面來分析這個表的做用。 1.先看最旁邊的註釋,說明的是該數組中對應的位置。 2.解釋這個數組中內容,這些數字怎麼來的。
舉例:0x53 如上圖所示的位置,爲何是0呢?咱們把0x53變成二進制來看: 0101 0011,從右往左看,第一個出現1的位,就是0位因此爲0. 爲何是從右往左看呢?由於高優先級的數字低,你應該懂的。
例子 : 有4個任務 ,優先級分別爲6,10,11,17.。把上面就緒任務算法再貼一遍:前面2位無論。
6對應二進制:000 110
高3位:000=0 經過OSMapTbl映射後,
OSMapTbl[prio>>3]= OSMapTbl[0]=0000 0001
低3位:110=6,經過OSMapTbl映射後
OSMapTbl[6]=0100 0000
OSRdyTbl[prio>>3]= OSRdyTbl[0]=0100 0000
10對應二進制:001 010
高3位:001=1 經過OSMapTbl映射後,
OSMapTbl[prio>>3]= OSMapTbl[1]=0000 0010.
低3位:010=2,經過OSMapTbl映射後
OSMapTbl[2]=0000 0100
OSRdyTbl[prio>>3]= OSRdyTbl[1]=0000 0100
11對應二進制:001 011
高3位:001=1 經過OSMapTbl映射後,
OSMapTbl[prio>>3]= OSMapTbl[1]=0000 0010.
低3位:011=3,經過OSMapTbl映射後
OSMapTbl[3]=0000 1000
OSRdyTbl[prio>>3]= OSRdyTbl[1]=0000 1000
17對應二進制:010 001
高3位:010=2 經過OSMapTbl映射後,
OSMapTbl[prio>>3]= OSMapTbl[2]=0000 0100.
低3位:001=1,經過OSMapTbl映射後
OSMapTbl[1]=0000 0010
OSRdyTbl[prio>>3]= OSRdyTbl[2]=0000 0010
經過就緒任務算法:
OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];
最終OSRdyGrp的值就是將全部的OSMapTbl[prio>>3]進行位或運算:
OSRdyGrp=
000 00001
|0000 0010
|0000 0010
|0000 0100
=0000 0111 = 0x07
OSRdyTbl[0]=0100 0000
OSRdyTbl[1]=
0000 0100
|0000 1000
=0000 1100(相同的列進行位或運算)
OSRdyTbl[2]=0000 0010
而後查找優先級斷定表OSUnMapTbl[]
OSRdyGrp=0x07
Y=OSUnMapTbl[0x07]=0
說明是最高優先級在第0組。
OSRdyTbl[0]=0100 0000=0x40
X = OSUnMapTbl[0x40]=6
最高優先級爲:prio= y*8+x=6
至此,最高優先級就選出來了。而後調度此任務運行就是了,另外,刪除任務就是將對應就緒列表位的置的1清零就是。
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio >> 3];
看到這裏,這行代碼理解應該沒有問題,就是反操做而已。