本篇通过几个方面来看deadline调度器:
1、deadline的设置
2、deadline的任务的添加
3、deadline的任务的调度
deadline的设置
deadline调度器不允许直接通过kernel_clone的方式创建,需要通过sched_setattr的方式将指定pid task修改为deadline调度器并进行必要参加的赋值,大体流程图如下:

首先通过系统调用sched_setattr主动来设置指定pid task的优先级别
SYSCALL_DEFINE3(sched_setattr, pid_t, pid, struct sched_attr __user *, uattr,
unsigned int, flags)
{
struct sched_attr attr;
struct task_struct *p;
int retval;
if (!uattr || pid < 0 || flags)
return -EINVAL;
retval = sched_copy_attr(uattr, &attr);
if (retval)
return retval;
if ((int)attr.sched_policy < 0)
return -EINVAL;
if (attr.sched_flags & SCHED_FLAG_KEEP_POLICY)
attr.sched_policy = SETPARAM_POLICY;
rcu_read_lock();
retval = -ESRCH;
p = find_process_by_pid(pid);
if (likely(p))
get_task_struct(p);
rcu_read_unlock();
if (likely(p)) {
if (attr.sched_flags & SCHED_FLAG_KEEP_PARAMS)
get_params(p, &attr);
retval = sched_setattr(p, &attr);
put_task_struct(p);
}
return retval;
}
__setscheduler_params:

void __setparam_dl(struct task_struct *p, const struct sched_attr *attr)
{
struct sched_dl_entity *dl_se = &p->dl;
dl_se->dl_runtime = attr->sched_runtime;
dl_se->dl_deadline = attr->sched_deadline;
dl_se->dl_period = attr->sched_period ?: dl_se->dl_deadline;
dl_se->flags = attr->sched_flags & SCHED_DL_FLAGS;
dl_se->dl_bw = to_ratio(dl_se->dl_period, dl_se->dl_runtime);
dl_se->dl_density = to_ratio(dl_se->dl_deadline, dl_se->dl_runtime);
}
__setscheduler_prio:
static void __setscheduler_prio(struct task_struct *p, int prio)
{
//根据优先级设定调度策略
if (dl_prio(prio))
p->sched_class = &dl_sched_class;
else if (rt_prio(prio))
p->sched_class = &rt_sched_class;
else
p->sched_class = &fair_sched_class;
p->prio = prio;
}
上述动作做完后,就将task加到指定的调度器任务中
static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
{
if (!(flags & ENQUEUE_NOCLOCK))
update_rq_clock(rq);
if (!(flags & ENQUEUE_RESTORE)) {
sched_info_enqueue(rq, p);
psi_enqueue(p, (flags & ENQUEUE_WAKEUP) && !(flags & ENQUEUE_MIGRATED));
}
uclamp_rq_inc(rq, p);
p->sched_class->enqueue_task(rq, p, flags);
if (sched_core_enabled(rq))
sched_core_enqueue(rq, p);
}
deadline的任务的添加
大概调用流程如下:
enqueue_task_dl -> enqueue_dl_entity -> __enqueue_dl_entity
-> rb_add_cached
这里对调用细节不作论述,只关注几个重要点:
一、deadline的设置
在上面已经有相关的配置了,但是相对值。还需要转化成需要数据

这里需要把设置的相对时间变成绝对截止时间
二、deadline核心的任务添加过程


从上面可知,deadline调用器。使用的红黑二叉树,左子树优先级最高。根据deadline值大小进行插入。插入完成后需要对二叉树作相关处理 ,以满足红黑二叉树特性。
deadline的任务的调度
在进行任务调度时候,如果deadline调度器有任务可供调度则会调用其pick_next_task_dl的回调函数
static struct task_struct *pick_next_task_dl(struct rq *rq)
{
struct task_struct *p;
p = pick_task_dl(rq);
//获取一个优化级最高的task就是上面所说的rb_leftmost
//#define rb_first_cached(root) (root)->rb_leftmost
if (p)
set_next_task_dl(rq, p, true);
return p;
}
static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
{
struct sched_dl_entity *dl_se = &p->dl;
struct dl_rq *dl_rq = &rq->dl;
p->se.exec_start = rq_clock_task(rq);//记录开始执行的时间
if (on_dl_rq(&p->dl))
update_stats_wait_end_dl(dl_rq, dl_se);
/* You can't push away the running task */
dequeue_pushable_dl_task(rq, p);//将此task从二叉树上移除,不能老是占位
if (!first)
return;
if (hrtick_enabled_dl(rq))
start_hrtick_dl(rq, p); //启动高精度定时器
if (rq->curr->sched_class != &dl_sched_class)
update_dl_rq_load_avg(rq_clock_pelt(rq), rq, 0);
deadline_queue_push_tasks(rq);
}
#ifdef CONFIG_SCHED_HRTICK
static void start_hrtick_dl(struct rq *rq, struct task_struct *p)
{
//定时器的超时时间为runtime,也就是说task运行时间为runtime大小
hrtick_start(rq, p->dl.runtime);
}
#else /* !CONFIG_SCHED_HRTICK */
static void start_hrtick_dl(struct rq *rq, struct task_struct *p)
{
}
#endif
下面来看看定时器超时的处理
一、定时器的初始化
static void hrtick_rq_init(struct rq *rq)
{
#ifdef CONFIG_SMP
INIT_CSD(&rq->hrtick_csd, __hrtick_start, rq);
#endif
hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
rq->hrtick_timer.function = hrtick;//超时回调函数
}
static enum hrtimer_restart hrtick(struct hrtimer *timer)
{
struct rq *rq = container_of(timer, struct rq, hrtick_timer);
struct rq_flags rf;
WARN_ON_ONCE(cpu_of(rq) != smp_processor_id());
rq_lock(rq, &rf);
update_rq_clock(rq);
//调用到了调度器的task_tick回调函数
rq->curr->sched_class->task_tick(rq, rq->curr, 1);
rq_unlock(rq, &rf);
return HRTIMER_NORESTART;
}
这里需要注意的是 task_tick这个函数在系统的tick 定时器里面也会调用

二、定时器超时处理
static void task_tick_dl(struct rq *rq, struct task_struct *p, int queued)
{
//更新当前状态,并修改runtime值大小为剩下时间
update_curr_dl(rq);
update_dl_rq_load_avg(rq_clock_pelt(rq), rq, 1);
/*
* Even when we have runtime, update_curr_dl() might have resulted in us
* not being the leftmost task anymore. In that case NEED_RESCHED will
* be set and schedule() will start a new hrtick for the next task.
*/
if (hrtick_enabled_dl(rq) && queued && p->dl.runtime > 0 &&
is_leftmost(p, &rq->dl))
//如果runtime时间大于0,则会restart此定时器,并使用最新的超时时间
start_hrtick_dl(rq, p);
}

上面的处理比较清晰,由于tick定时器会不断触发这里的runtime值为持续减少。如果runtime小于等于0或者调用yield_task_dl回调函数,都会将当前处理的task移除二叉树。并重新进行任务调度。
deadline任务调度器核心是任务的添加、移除、调度。这里的二叉树的使用策略搞清楚,大体就能明白deadline的调度器使用逻辑了
Deadline调度器详解
641

被折叠的 条评论
为什么被折叠?



