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的调度器使用逻辑了
