目 錄
知識簡介
嵌入式實時操作系統
SylixOS操作系統是實時多任務嵌入式操作系統,所謂實時,是指系統的正確性不僅僅依賴於計算機的邏輯結果而且依賴於結果產生的時間。結果產生的時間就是通常所說的截止期限,描述系統實時性的指標主要有:
對緊急時間可預見性的快速響應;
高度的可調度性,所謂調度性是指系統在任務時間需求能夠滿足下的最高資源利用率,也就是平均每秒的及時執行的任務數量;
在暫時超負荷情況下的系統穩定性,即當系統超負荷運轉導致不能滿足所有任務的截止期限需求時,仍然能夠保證關鍵任務的截止期限需求;
對於實時而言,截止期限的要求是必須得到滿足的,但是區分具體應用場合,這種時限要求的嚴格成都又有所不同。如果這種要求是絕對的,即不滿足截止期限的要求計算結果就毫無意義甚至可能造成無法預料的結果或系統致命的錯誤,那就稱之爲硬實時系統(Hard Real Time System);SylixOS就是硬實時系統。在硬實時系統中如果出現了這樣的情況就意味着巨大的損失和災難,比如說日本福島核電站中的堆芯溫度控制系統如果沒有對堆芯過熱做出及時的冷卻處理,後果不堪設想。當不滿足截止期限的要求時計算機結果的可調度性逐漸減弱但是並不足以造成嚴重後果,系統仍然繼續調度直至任務完成,則稱爲軟實時系統(Soft Real Time System)。軟實時系統是指如果在系統負荷較重的時候允許發生錯過截止期限的情況而且不會造成太大的危害。硬實時系統和軟實時系統的實現區別主要是,在選擇調度算法上選擇基於優先級調度的算法是以滿足軟實時系統的需求而且可以提供告訴的響應和大的系統吞吐率,而對硬實時系統來說需要使用的算法就應該是調度方式簡單反應速度快的實時調度算法了。
優先級調度算法
調度,是內核的主要職責之一,就是確定該輪到那個任務運行了。優先級搶佔調度算法中,每個任務根據其重要程度的不同而被賦予一定的優先級。優先級搶佔調度算法是指系統運行過程中高優先級的任務可以中斷低優先級的任務,讓處在就緒態的優先級最高的任務先運行。目前多數實時內核採用優先級可搶佔的調度算法,主要原因有以下幾點:
首先,處理異常的任務可能需要搶佔正在運行的任務,以便及時對異常做出響應;
第二,任務的重要性不同,優先級可搶佔使一些重要任務有可能搶佔正在運行的任務;
第三,允許任務搶佔可得到更爲有效的調度;
根據不同的優先級分配方法,基於優先級的調度算法可以分爲如下兩種類型:
靜態調度:
靜態調度是在系統開始運行前進行調度的,嚴格的靜態調度在系統運行時無法對任務進行重新調度。靜態調度的怒表是把任務分配到各個CPU,並對每一個CPU給出所要運行任務的靜態運行順序。靜態調度算法實現簡單,調度的額外開銷小,在系統超載時可預測性好。但也具有很大的侷限性,例如資源利用率低、受系統支持的優先級個數限制以及靈活性和自適應性差等。
2.動態調度:
在嵌入式實時系統中,動態調度依賴於任務的優先級。優先級可以靜態分配或者依據不同的特徵參數,如截止時間、空閒時間或關鍵性(即任務的重要程度)等進行動態分配。動態調度可以是搶佔式的或非搶佔式的。當檢查到一事件時,動態搶佔式算法立即決定是運行與此時間相關的任務,或繼續執行當前的任務;對於動態非搶佔式算法,它僅僅知道有另一個任務可以運行,在當前任務結束後,它纔在就緒的任務中選擇一個來運行。
單調速率調度算法RMS(Rate Monotonic Scheduling)爲每個週期線程指定一個固定不變的優先級,週期最短的線程優先級最高。週期越短,線程的到達頻率越高,優先級也越高,這正是此策略被稱爲速率單調算法的原因。RMS算法也可用於多CPU環境,用於分配任務優先級。這種方法基於哪個任務執行的次數最頻繁,執行最頻繁的任務優先級最高。
RMS的由來是從硬實時環境的初始定義開始的:
所有的任務都是週期性的,各個任務請求的截止期限呈週期性,同時具有恆定的時間間隔;
所有任務都必須在下一次任務請求到來之前完成;
所有的任務都是獨立的,每一次任務請求不依賴於其他任務的執行或者初始化;
每個任務都存在一個恆定且非時變的執行時間,即CPU不間斷地執行該任務的時間;
任何非週期的任務屬於特殊任務,它們屬於初始化或者是恢復錯誤的時間,僅當它們自身執行的時候才恩能夠取代週期性任務,同時非週期性任務不存在有截止期限。
技術實現
函數原型分析
#include <sched_rms.h>
int sched_rms_init(sched_rms_t *prms, pthread_t thread);
int sched_rms_destroy(sched_rms_t *prms);
int sched_rms_period(sched_rms_t *prms, const struct timespec *period);函數 sched_rms_init 原型分析:
l 此函數成功返回 0,失敗返回-1 並設置錯誤號;
l 參數 prms 是 RMS 調度器指針;
l 參數 thread 是調用線程的句柄。
函數 sched_rms_destroy 原型分析:
l 此函數成功返回 0,失敗返回-1 並設置錯誤號;
l 參數 prms 是 RMS 調度器指針;函數 sched_rms_period 原型分析:
l 此函數成功返回 0,失敗返回-1 並設置錯誤號;
l 參數 prms 是 RMS 調度器指針;
l 參數 period 是程序執行週期。使用流程
SylixOS中RMS調度器使用流程如圖 2-1所示。
圖 2-1RMS調度器使用流程圖
源碼分析
初始化調度器時,如圖 2-2所示調用API_ThreadSetSchedParam()函數設置線程調度參數,首先將線程調度策略設置爲FIFO(先入先出);然後將該線程設置爲LW_OPTION_RESPOND_IMMIEDIA(高速響應線程),該線程就緒後會加入到就緒環頭部。(就緒環概念請參考《SylixOS線程調度淺析》文檔)
if (API_ThreadSetSchedParam(thread, LW_OPTION_SCHED_FIFO,
LW_OPTION_RESPOND_IMMIEDIA)) {
errno = ESRCH;
return (PX_ERROR);
}
圖 2-2設置線程參數
啓動調度器時,如圖 2-3所示,第一次執行時間是不準確的,首先使用lib_clock_gettime()函數獲得第一次啓動時時間。等第二次執行時依然使用lib_clock_gettime()函數獲得第二次啓動時間,計算兩次執行時間間隔;然後通過__timespecSub()函數計算時間間隔與所定週期的時間差;然後調用__timespecAdd()函數使程序以確定週期運行;這時可以確定程序運行與所定週期的時間差,最後調用nanosleep()函數使該線程睡眠此時間差時間。這樣就能保證調度器自動調整時間。
lib_clock_gettime(CLOCK_MONOTONIC, &temp); /* 獲得當前時間 */
etime = temp;
__timespecSub(&etime, &prms->PRMS_tsSave);
if (__timespecLeftTime(period, &etime)) { /* 執行時間超過週期 */
lib_clock_gettime(CLOCK_MONOTONIC, &prms->PRMS_tsSave); /* 獲得當前時間 */
errno = EOVERFLOW;
return (PX_ERROR);
}
temp = *period;
__timespecSub(&temp, &etime);
/*
* 注意: 這裏直接加上週期是爲了讓每次測算都是以一個固定週期律進行
* 提高週期精度. (不使用 lib_clock_gettime())
*/
__timespecAdd(&prms->PRMS_tsSave, period); /* 以確定週期運行 */
return (nanosleep(&temp, LW_NULL));
圖 2-3啓動調度器過程
調度器銷燬時直接將RMS結構體sched_rms_t成員PRMS_iStatus置0。
示例演示
如圖 2-4 所示爲RMS使用用例,設置RMS調度器時間爲3秒,代碼執行時間隨機產生,最終測試結果爲3秒打印一次。
#include <stdio.h>
#include <sched_rms.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
sched_rms_t rms;
void process_func(void) /* 執行函數 */
{
srand(time(NULL)); /* 產生隨機數 */
int i = rand()%2;
for (; i >= 0; --i) { /* 隨機睡眠一段時間 */
sleep(1);
}
}
void *rms_thread ()
{
pthread_t tid;
struct timespec period; /* 設置調度週期爲3秒 */
period.tv_nsec = 0;
period.tv_sec = 3;
tid = pthread_self(); /* 獲得自身tid */
sched_rms_init(&rms, tid); /* 初始化RMS調度器 */
while (1) {
if (sched_rms_period(&rms, &period) != 0) { /* 啓動RMS調度器 */
break;
}
fprintf(stdout, "rms thread running...\n");
process_func();
}
return (NULL);
}
int main (int argc, char *argv[])
{
pthread_t tid;
int ret;
ret = pthread_create(&tid, NULL, rms_thread, NULL); /* 創建線程 */
if (ret < 0) {
fprintf(stderr, "pthread_create error.\n");
return (-1);
}
pthread_join(tid, NULL);
sched_rms_destroy(&rms); /* 銷燬RMS調度器 */
return (0);
}
圖 2-4 使用用例