為了弄清楚在多cpu系統中是如何實現實時調度的,先引入以下幾個概念:
cpu的狀態:
我們知道,在linux系統中,任務的優先級為0~140。
INVALID:(-1)該cpu不可用
IDLE(0):140
NORMAL(1):100~139對應于普通任務的優先級
RT0~RT99(2~102):對應于實時任務的優先級
進程優先級:
在linux內核中,每個進程都有一個task_struct,其中與優先級相關的屬性包括
1、prio:對于非實時進程而言prio==normal_prio=99-rt_priority(prio的值越大,則進程的優先級越小)
2、normal_prio:99-rt_priority
3、static_prio:=100+nice
4、rt_priority:0:非實時任務;[1,99]實時任務,值越大,優先級越高。
在調度時使用了prio,其數值0對應最高優先級,99為最低實時優先級。Prio和normal_prio數值越大優先級越小,而rt_priority的數值越大優先級越大。
task balance:
在多cpu系統中,為了保證各個cpu上實時任務的負載均衡,引入了push和pull操作:
1、push操作:
當當前cpu上有多于一個的實時任務,那么需要使用pull操作看看是否可以將還未運行的實時任務移動到其他的cpu上,主要操作由push_rt_task函數完成。static?int?push_rt_task(struct?rq?*rq)
{
struct?task_struct?*next_task;
struct?rq?*lowest_rq;
int?ret?=?0;
///如果當前隊列不是超載狀態,則直接返回
if?(!rq->rt.overloaded)
return?0;
///選擇rq中下一個進程
next_task?=?pick_next_pushable_task(rq);
if?(!next_task)
return?0;
retry:
......
///如果下一個進程的優先級比當前進程的優先級高,那么需要執行的不是push操作而是重新調度
if?(unlikely(next_task->prio?curr->prio))?{
resched_task(rq->curr);
return?0;
}
......
///尋找那個cpu的rq符合條件,將其rq上鎖
lowest_rq?=?find_lock_lowest_rq(next_task,?rq);
///如果沒有找到合適的rq,我們則需要判斷:到底還要不要再找,因為在find_lock_lowest_rq函數中釋放了當前rq上的鎖,因此可能會導致當前rq上沒有需要push的任務,在這種情況下我們就不用再試,如果需要push的任務還在,那么則進入retry繼續嘗試。
if?(!lowest_rq)?{
struct?task_struct?*task;
task?=?pick_next_pushable_task(rq);
if?(task_cpu(next_task)?==?rq->cpu?&&?task?==?next_task)?{
goto?out;
}
if?(!task)
goto?out;
put_task_struct(next_task);
next_task?=?task;
goto?retry;
}
///執行任務遷移相關的工作。
deactivate_task(rq,?next_task,?0);
set_task_cpu(next_task,?lowest_rq->cpu);
activate_task(lowest_rq,?next_task,?0);
ret?=?1;
resched_task(lowest_rq->curr);
double_unlock_balance(rq,?lowest_rq);
out:
put_task_struct(next_task);
return?ret;
}
2、pull操作
與push操作相反,pull操作用于當前cpu的rq比較空閑,想要主動調入實時任務,該操作主要由
pull_rt_task完成。static?int?pull_rt_task(struct?rq?*this_rq)
{
int?this_cpu?=?this_rq->cpu,?ret?=?0,?cpu;
struct?task_struct?*p;
struct?rq?*src_rq;
if?(likely(!rt_overloaded(this_rq)))
return?0;
///查找每一個超載的cpu
for_each_cpu(cpu,?this_rq->rd->rto_mask)?{
......
///src_rq:需要pull的cpu。
src_rq?=?cpu_rq(cpu);
///如果src_rq的下一個任務的優先級高于當前cpu的優先級,則什么都不用做,因為src_cpu會主動執行pull操作。
if?(src_rq->rt.highest_prio.next?>=
this_rq->rt.highest_prio.curr)
continue;
double_lock_balance(this_rq,?src_rq);
///判斷是否有需要被pull的任務,沒有則退出
if?(src_rq->rt.rt_nr_running?<=?1)
goto?skip;
///找到src_rq上最高優先級的任務
p?=?pick_next_highest_task_rt(src_rq,?this_cpu);
if?(p?&&?(p->prio?rt.highest_prio.curr))?{
WARN_ON(p?==?src_rq->curr);
WARN_ON(!p->on_rq);
if?(p->prio?curr->prio)
goto?skip;
ret?=?1;
deactivate_task(src_rq,?p,?0);
set_task_cpu(p,?this_cpu);
activate_task(this_rq,?p,?0);
}
skip:
double_unlock_balance(this_rq,?src_rq);
}
return?ret;
}