ucosii實時作業系統的任務排程

2021-07-14 22:23:29 字數 4142 閱讀 6511

嵌入式作業系統的任務排程演算法好壞在很大程度上決定了該系統的執行效率,由於其執行的頻率極高,所以在任務排程函式的實現上,對於效率的要求可以用苛刻來形容。基於優先順序的任務排程總結下來就是做了兩件事:

1 找到優先順序最高的就緒態任務

2 切換任務上下文並開始執行該任務

第二步的切換上下文對個各個rtos區別不大,而且是一套「固定動作」,真正有意思的地方是在第一步。本文以ucosii為藍本,講解任務排程中最重要的第1步,如何最快的找到優先順序最高的就緒態任務。其實邵貝貝老師的那本書《嵌入式實時作業系統μc/os-ii 》(第2版)已經很詳細的講解了整個過程。但是樓主思考再三還是決定按照自己的理解重寫一遍,盡量以淺顯易懂的語言描述出來,讓更多的人理解這個看起來有點神秘、理解了以後就覺得蠻有意思的過程。

先說明和ucosii任務排程的一些基本情況。ucosii 2.52最多有64個任務,每個任務都要求有不同的優先順序,優先順序的數字越小代表優先順序越高(優先順序為0的task高於為1的task)。大家都知道任務有就緒、執行、睡眠、等待掛起等多個狀態,對於排程器來說,任務只有兩種狀態:就緒和非就緒。於是很自然的,我們想到要定義個64位元組長度的陣列,裡面的每個位元組對應於乙個任務,陣列項為1來表示該任務就緒,0表示非就緒。

char taskarray[64];

舉個例子:

taskarray[64] = ,就表示第1、2、64三個任務是就緒的,其餘任務都是非就緒的。

有一定經驗的程式設計師可能已經發現,用64位元組太浪費了,每個任務只需要乙個bit就夠了,於是乎 osrdytbl就可以

就閃亮登場了,它是任務排程演算法的第乙個「關鍵人物」。

char osrdytbl[8]; 

這裡需要注意,雖然這個陣列是8個位元組,每個位元組有8個bit。

那麼問題來了,

這些bit位在計算機記憶體裡是怎麼排放的?下面這種排法毫無疑問是

最舒服最

直觀的的方式:

但是很可惜,電腦程式對於資料的解讀和人的習慣是有區別的。程式一般都是以左移(1<< x)來表明第x位的

例如,osrdytbl[

0] |= (1 <

3);           表明把第0個元素的第3位置1。

if (osrdytbl[

4] & (1 <

)       表示判斷第4個元素的第5位是否為1

所以,這些bit位在記憶體裡的存放方法應該如下所示:

為方面**,我們把它表示為正方形的形式,如下所示:

右上角0表示最高優先順序,左下角63表示最低優先順序。在同一行裡,右邊的格比左邊的格優先順序要高。

裡灰色的方塊表示該優先順序的任務就緒,其餘的任務都是非就緒。

接下來,我們就可以集中火力開始「以最快的速度」查詢優先順序最高的任務了

有了上面的這張表,大多數人第一反應就是通過迴圈遍歷的方法來查詢距離左上角最近的為1的方框,從第一行的左邊開始,一格一格的檢視,第一行查完了再查第二行,找到第乙個為1的格就可以返回了,**實現大概是這個樣子的

for (int i = 0;i < 8;i++)

for (int j = 0;j < 8;j++)

if (osrdytbl[i] & (1 << j))

break;

在樓主給出的這個例子中,最高優先順序是15,需要從0開始一直迴圈到15,看起來還不算太多。但如果,系統中只有倆task就緒,優先順序分別是62和63呢?那每次排程器豈不是都要迴圈六十多次就為找乙個task執行,這樣好像不太好吧這裡插講一下,得到了行和列之後,怎麼算出優先順序。其實和數學計算一樣

priority = i*8 + j;         //第1行第7列,結果就是15

很明顯,前面那個查詢演算法太慢了,排程器本身就占用了太多的cpu,我們需要更快的查詢方法

多看幾眼那個正方形的優先順序方格矩陣,我們不難想到,能不能先定位到最高優先順序的task在哪一行,在從這行的開頭找過去呢?

這是個不錯的思路,於是乎我們需要請出下一位「關鍵人物」:

char osrdygrp;

它是乙個8位元的數值,每個位元可以對應正方形矩陣裡的每一行,如果該bit為1,則表明這一行裡有任務就緒。

當osrdytbl[0]裡至少有一位為1時,osrdygrp的bit 0位1,當osrdytbl[1]裡至少有一位為1時,osrdygrp的bit 1位1,以此類推。

樓主的這個例子中,第1,2,3,4,7都有任務就緒,其餘行沒有,所以osrdygrp應該為0x10011110.

有了這個結構,新的查詢演算法看起應該是這個樣子的

for (int i = 0; i < 8;i++)

if (osrdygrp & (1 << i))

break;

for (int j = 0;j < 8;j++)

if (osrdytbl[i] & (1 << j))

break;

第乙個迴圈用於定位task在哪一行,第二個迴圈用於定位在哪一列。這個演算法比前乙個要快了很多,即使在最壞的情況下,只有優

先級為63的乙個task需要排程,需要16次迴圈。

我們完成了從 8*8 到 8+8 的進步

前面的兩級遍歷查詢法,看起來還不錯,在一般的應用場景,已經夠用了。但是在排程器這種對效率極為敏感的地方,還需要再努把力。再分析一下這兩個迴圈都幹了什麼:

第乙個迴圈,從右向左遍歷乙個8位欄位,找出最近的1作為結果;第二個迴圈,也是乙個從類似的過程。兩次迴圈的「輸入」都是乙個位元組的數字,「輸出」是乙個結果「距離最右邊最近的1是在第幾位」。8位字段的輸入,一共就256種可能性,如果我們把這256種結果都提前算好儲存在乙個表裡,那不是可以省去遍歷的過程,直接查表就能得出結果了?

想到這裡,該請出今天的最後一位「關鍵人物」了,就是osunmaptbl

它是乙個const型的陣列,說明它裡面的每一項都是提前算好的。正如前面分析,這個表裡的每一項都是表達乙個該索引代表的數「距離最右邊最近的1是在第幾位」。

以樓主畫圈的兩個數為例,第乙個是第4排第1列,其索引為48,二進位制00110000,距離最右邊最近的1就是第4位

第二個是第7排第9列,其索引為104,二進位制10011000,距離最右邊最近的1就是第3位。

現在我們可以很清楚的知道,在排程器執行函式os_sched()裡的下面這幾句話是幹什麼的了:

y             = osunmaptbl[osrdygrp];        /* find highest priority's task priority number   */

x = osunmaptbl[osrdytbl[y]];

ospriohighrdy = (int8u)((y << 3) + x);

第一行:以osrdygrp為索引,查詢osunmaptbl以定位到osrdytbl裡的距離0最近的有bit位為1的行

第二行:以osrdytbl裡的這一行(它也是乙個8位的數字)為索引,查出這一行裡,距離最右邊最近的有bit位為1的列

第三行:將行和列組合起來,得到的就是最終我們需要的「優先順序」。

由於ucosii裡每個優先順序只能有乙個任務,所以有了優先順序就等於定位到了該任務。

就這樣,是不是還蠻簡單的

?如果您讀到這裡,還覺得意猶未盡,請看下面幾個思考題,答案在這篇博文裡freertos的任務排程演算法優化實現

1 ucosii 2.52系統內部設定了最多隻支援64個優先順序,如果換了乙個rtos,任務一共有256個優先順序,還是有這個查詢方法,應該做什麼修改?

2 ucosii 2.52系統強行規定了每個優先順序都只能有乙個任務,所以找到了最高優先級數,就等於找到了任務。如果換了乙個rtos,每個優先順序上允許有多個任務,那麼在查詢到最高優先級數之後,在相同優先順序的多個任務中如何確定你本次要排程的那個任務?

3 ucosii裡是認為優先順序數值越大,其任務優先順序越小,如果反過了,數值越大則優先順序越大應該怎麼處理?

實時作業系統之任務

任務可以以下列狀態之一存在 有效的任務狀態轉換 任務優先順序 每個任務都分配了乙個從0到 configmax priorities 1 的優先順序,其中configmax priorities在freertosconfig.h中定義。如果正在使用的埠實現了使用 計數前導零 型別指令 用於在單條指令中...

ucos ii作業系統

ucos ii是乙個多工的作業系統,其最大優點即為實時性。任務通常是乙個無限的迴圈,其中包括了使用者 而實時性即指最快的響應優先順序最高的任務。確實,對於乙個初步接觸ucos ii的新手來講,想要完全理解是要花時間的。我們想用ucos ii,在移植成功後 以後在寫移植方面吧 我們首先就得進行初始化,...

uCOS II作業系統

簡介 ucos ii是乙個簡單 高效的嵌入式實時作業系統核心 支援x86 arm powerpc mips等多種體系結構 www.ucos ii.com ucos ii的各種商業應用 醫療器械 移動 路由器 工業控制 gps 導航系統 智慧型儀器 更多 計算機作業系統的作用 從使用者的角度來看它就是...