點選打開連結
1,實時程序和普通程序排程的差别
實時程序需要嚴格按照優先級的順序執行,比如在八核平台上,必須是優先級最高的八個程序得到排程,如果此時八個優先級最高的程序都在某一個cpu的rt隊列上,那麼此時的排程就會涉及到了程序在不同cpu的遷移。
2,實時排程算法概述
該實時排程器主要為了解決以下四種情況:
(1). 在喚醒任務時,待喚醒的任務放置到哪個運作隊列最合适(這裡稱為pre-balance);
(2). 新喚醒任務的優先級比某個運作隊列的目前任務更低時,怎麼處理這個更低優先級任務;
(3). 新喚醒任務的優先級比同一運作隊列的某個任務更高時,并且搶占了該低優先級任務,該低優先級任務怎麼處理?
(4). 當某個任務降低自身優先級,導緻原來更低優先級任務相比之下具有更高優先級,這種情況怎麼處理。
對于情況2和情況3,實時排程器采用push操作。push操作從根域中所有運作隊列中挑選一個運作隊列(一個cpu對應一個運作隊列),該運作隊列的優先級比待push任務的優先級更低。運作隊列的優先級是指該運作隊列上所有任務的最高優先級。
對于情況4,實時排程器采用pull操作。當某個運作隊列上準備排程時,候選任務比目前任務的優先級更低時,實時排程器檢查其他運作隊列,确定是否可以pull更高優先級任務到本運作隊列。還有,當某個運作隊列上發生排程時,該運作隊列上沒有任務比目前任務優先級高,實時排程器執行pull操作,從其他運作隊列中pull更高優先級任務到本運作隊列。
每CPU變量運作隊列rq,包含一個rt_rq資料結構。rt_rq結構體主要内容如下:
struct rt_rq {
struct rt_prio_array active;
...
unsigned long rt_nr_running; // 可運作實時任務個數
unsigned long rt_nr_migratory; // 該運作隊列上可以遷移到其他運作隊列的實時任務個數
unsigned long rt_nr_uninterruptible;
int highest_prio;
int overloaded;
};
實時任務優先級範圍為0到99。這些實時任務組織成優先級索引數組active,該優先級數組的資料結構類型為rt_prio_arry。rt_prio_arry資料結構由兩部分組成,一部分是位圖,
另一部分是數組。
struct rt_prio_arry {
unsigned long bitmap[BITS_TO_LONGS(MAX_RT_PRIO+1)];
struct list_head queue[MAX_RT_PRIO];
}
3,根域的概念
cpusets提供了一種機制,cpusets可以将部分CPU劃入一個子集合,這個CPU子集供某個任務或者某個任務組使用。各個cpusets之間可以重疊,如果某個cpuset與其他cpuset沒有重複CPU,那麼将該cpuset稱為獨立的。每個獨立的cpuset定義為一個孤立的域(根域)。與根域有關的資訊儲存在資料結構root_domain中。
struct root_domain {
atomic_t refcount; /* reference count for the domain */
cpumask_t span; /* span of member cpus of the domain*/
cpumask_t online; /* number of online cpus in the domain*/
cpumask_t rto_mask; /* mask of overloaded cpus in the domain*/
atomic_t rto_count; /* number of overloaded cpus */
....
};
4,cpu的優先級管理
每個CPU可以處于如下任何一種狀态:INVALID,IDLE,NORMAL,RT1,...,RT99,處于INVALID狀态的CPU不适合任務路由。系統使用2維位圖來維護這些狀态資訊。第1個位圖用于處于同一優先級的不同CPU,CPU優先級是指rt_rq結構中的highest_prio變量;第2個位圖用于不同的優先級。
#define CPUPRI_NR_PRIORITIES (MAX_RT_PRIO + 2)
struct cpupri {
struct cpupri_vec pri_to_cpu[CPUPRI_NR_PRIORITIES]; //pri_to_cpu位圖可以快速找到優先級最低的cpu集合
int cpu_to_pri[NR_CPUS]; // 各個cpu對應優先級
};
5,push和pull算法
push算法
push_rt_task - 把目前run_queue中多餘的實時程序推給其他run_queue。需要滿足以下條件:
1、每次push一個程序,這個程序的優先級在目前run_queue中是第二高的(優先級最高的程序必定正在運作,不需要移動);
2、目标run_queue上正在運作的不是實時程序(是普通程序),或者是top-N中優先級最低的實時程序,且優先級低于被push的程序;
3、被push的程序允許在目标CPU上運作(沒有親和性限制);
4、滿足條件的目标run_queue可能存在多個(可能多個CPU上都沒有實時程序在運作),應該選擇與目前CPU最具親緣性的一組CPU中的第一個CPU所對應的run_queue作為push的目标(順着sched_domain--排程域--逐漸往上,找到第一個包含目标CPU的sched_domain。見後面關于sched_domain的描述);
該函數會在以下時間點被調用:
1、非正在運作的普通程序變成實時程序時(比如通過sched_setscheduler系統調用);
2、發生排程之後(這時候可能有一個實時程序被更高優先級的實時程序搶占了);
3、實時程序被喚醒之後,如果不能馬上在目前CPU上運作(它不是目前CPU上優先級最高的程序);
pull算法
pull_rt_task - 把其他CPU的run_queue中的實時程序pull過來,放到目前CPU的run_queue中。被pull過來的實時程序要滿足以下條件:
1、程序是其所在的run_queue中優先級第二高的(優先級最高的程序必定正在運作,不需要移動);
2、程序的優先級比目前run_queue中最高優先級的程序還要高;
3、程序允許在目前CPU上運作(沒有親和性限制);
該函數會在以下時間點被調用:
1、發生排程之前,如果prev程序(将要被替換下去的程序)是實時程序,且優先級高于目前run_queue中優先級最高的實時程序(這說明prev程序已經離開TASK_RUNNING狀态了,否則它不會讓位于比它優先級低的程序);
2、正在運作的實時程序優先級被調低時(比如通過sched_setparam系統調用);
3、正在運作的實時程序變成普通程序時(比如通過sched_setscheduler系統調用);