deadline调度学习

Deadline调度器详解

本篇通过几个方面来看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的调度器使用逻辑了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruk_spp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值