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给普通进程)是不现实的。