deadline调度学习

📅 2026/7/30 14:25:46 👁️ 阅读次数 📝 编程学习
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,structsched_attr__user*,uattr,unsignedint,flags){structsched_attrattr;structtask_struct*p;intretval;if(!uattr||pid<0||flags)return-EINVAL;retval=sched_copy_attr(uattr,&attr);if(retval)returnretval;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);}returnretval;}

__setscheduler_params:

void__setparam_dl(structtask_struct*p,conststructsched_attr*attr){structsched_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:

staticvoid__setscheduler_prio(structtask_struct*p,intprio){//根据优先级设定调度策略if(dl_prio(prio))p->sched_class=&dl_sched_class;elseif(rt_prio(prio))p->sched_class=&rt_sched_class;elsep->sched_class=&fair_sched_class;p->prio=prio;}

上述动作做完后,就将task加到指定的调度器任务中

staticinlinevoidenqueue_task(structrq*rq,structtask_struct*p,intflags){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的回调函数

staticstructtask_struct*pick_next_task_dl(structrq*rq){structtask_struct*p;p=pick_task_dl(rq);//获取一个优化级最高的task就是上面所说的rb_leftmost//#define rb_first_cached(root) (root)->rb_leftmostif(p)set_next_task_dl(rq,p,true);returnp;}staticvoidset_next_task_dl(structrq*rq,structtask_struct*p,bool first){structsched_dl_entity*dl_se=&p->dl;structdl_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);}#ifdefCONFIG_SCHED_HRTICKstaticvoidstart_hrtick_dl(structrq*rq,structtask_struct*p){//定时器的超时时间为runtime,也就是说task运行时间为runtime大小hrtick_start(rq,p->dl.runtime);}#else/* !CONFIG_SCHED_HRTICK */staticvoidstart_hrtick_dl(structrq*rq,structtask_struct*p){}#endif

下面来看看定时器超时的处理
一、定时器的初始化

staticvoidhrtick_rq_init(structrq*rq){#ifdefCONFIG_SMPINIT_CSD(&rq->hrtick_csd,__hrtick_start,rq);#endifhrtimer_init(&rq->hrtick_timer,CLOCK_MONOTONIC,HRTIMER_MODE_REL_HARD);rq->hrtick_timer.function=hrtick;//超时回调函数}
staticenumhrtimer_restarthrtick(structhrtimer*timer){structrq*rq=container_of(timer,structrq,hrtick_timer);structrq_flagsrf;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);returnHRTIMER_NORESTART;}

这里需要注意的是 task_tick这个函数在系统的tick 定时器里面也会调用

二、定时器超时处理

staticvoidtask_tick_dl(structrq*rq,structtask_struct*p,intqueued){//更新当前状态,并修改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的调度器使用逻辑了