1.內核如何組織調度實體:
struct task_struct {
...
struct sched_rt_entity rt;
...
}
struct sched_rt_entity {
struct list_head run_list;
unsigned long timeout;
unsigned long watchdog_stamp;
unsigned int time_slice;
unsigned short on_rq;
unsigned short on_list;
struct sched_rt_entity *back;
#ifdef CONFIG_RT_GROUP_SCHED
...
#endif
}
2.進程的實時調度實體是怎麼加入到cpu的rt_rq的?
static void __enqueue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags)
{
//通過當前rq_se找到當前cpu中的per_cpu變量runqueues中的rt_rq
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
//得到rt_prio_array
struct rt_prio_array *array = &rt_rq->active;
struct rt_rq *group_rq = group_rt_rq(rt_se);
//根據優先級找到對應的queue
struct list_head *queue = array->queue + rt_se_prio(rt_se);
//入隊queue
if (move_entity(flags)) {
WARN_ON_ONCE(rt_se->on_list);
if (flags & ENQUEUE_HEAD)
list_add(&rt_se->run_list, queue);
else
list_add_tail(&rt_se->run_list, queue);
__set_bit(rt_se_prio(rt_se), array->bitmap);
rt_se->on_list = 1;
}
//標記當前進程在隊中
rt_se->on_rq = 1;
//增加rt_rq的統計值
inc_rt_tasks(rt_se, rt_rq);
}
3.關於時間片:
fork進程時,不論是不是實時進程,rt相關的成員都會進行初始化:
static void __sched_fork(unsigned long clone_flags, struct task_struct *p)
{
INIT_LIST_HEAD(&p->rt.run_list);
p->rt.timeout = 0;
p->rt.time_slice = sched_rr_timeslice;
p->rt.on_rq = 0;
p->rt.on_list = 0;
|
}
//實時調度器默認時間片100毫秒():
#define RR_TIMESLICE (100 * HZ / 1000)
實時爲何如此之大的原因後面會提到
4.爲什麼說實時調度器是非常霸道?
它的調度器類優先級是僅次於deadline調度類的:
#define sched_class_highest (&stop_sched_class)
stop_sched_class.next = &stop_sched_class->next = \
&dl_sched_class->next = \
&rt_sched_class->next = \
&fair_sched_class->next = \
&idle_sched_class->next = \
NULL
//pick_next_task核心邏輯
for_each_class(class) {
p = class->pick_next_task(rq, prev, rf);
}
就像銀行排號一樣,一旦來了一個白金卡用戶,他會立即排到所有普通卡用戶前頭。所以在執行調度時,一旦有實時進程的存在,就優先調度實時進程。
再看看實時進程有沒有可能被搶佔,週期性調度器會調用當前進程調度類的task_tick來決定當前進程是否要搶佔,看看實時調度類的實現task_tick_rt:
static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)
{
if (p->policy != SCHED_RR)//SCHED_FIFO
return;
if (--p->rt.time_slice)//SCHED_RR
return;
//即使是RR進程的時間片用完,它也只是重新填上時間片再加到同實時優先級run_list的隊尾而已
p->rt.time_slice = sched_rr_timeslice;
for_each_sched_rt_entity(rt_se) {
if (rt_se->run_list.prev != rt_se->run_list.next) {
requeue_task_rt(rq, p, 0);
resched_curr(rq);
return;
}
}
}
惡霸的原則就是誰拳頭硬(有效優先級高)誰就先說話:
static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flags)
{
if (p->prio < rq->curr->prio) {
resched_curr(rq);
return;
}
}
As we know,實時進程優先級是肯定是要高於普通進程的:
綜上,大多數情況下,惡霸(實時進程)一旦來了,除非他自己捐出所有家產歸隱山林(主動讓出CPU並離開運行列隊),打倒惡霸開倉放糧給老百姓(搶佔實時進程騰出CPU給普通進程)是不現實的。