SCHED_DEALINE调度类分析(一)

以下是本人分析的内核新的调度类SCHED_DEADLINE调度类,这个调度类没有进主线之前叫EDF(Earliest DeadlineFirst)。在3.14的内核时进入了主线。本文是基于没有进主线之前的分析。思想基本一致。
EDF介绍
EDF(Earliest DeadlineFirst)它是一种最早到期优先的调度算法。当前Linux内核中使用的调度的算法是主要是cfs和fifo调度类,但是这两种算法的应用都是在一定场景下是最好的,有一定的局限性。当应用需要更精确的时间响应和调度方法的时候(比如多媒体业务等),就无法胜任了。
EDF的基本思想
首先,介绍edf中涉及的概念[1]:
Wakeup time: 时钟中断到期的时间;
Event response time: 调度的时延;
Deadline:任务到期的时间;
runtime: 任务运行的时间,就是deadline到期前必须运行的时间。也叫budget;
period:任务的周期时间,一般等于deadline;
下面通过如解释一下上面的概念:
这里写图片描述
图1 EDF结构图

EDF算法是以deadline为优先级,在deadline期间内必须运行完预定的配额。精度区别于时钟,x86的时钟是很精确的。每个cpu都有专门的定时器。Linux的EDF在x86上的精度可以精确到10us。
EDF样例:
Task1:runtime 1ms deadline 8ms
Task2:runtime 2ms deadline 5ms
Task3: runtime 4ms deadline 10ms
他们运行的时序如图
这里写图片描述
图2 运行时序图
EDF方案是内核态的调度方案,其定时是准的,正因为这样用户态的业务程序运行的时间就长了。表现出来的就是业务的转发能力增强了Edf的关键的特性如下:
每个任务的时间的行为相互独立,比如到期时间,不会受到系统中其他的任务或者任务数量的影响每个任务的行为由如下几个变量变量定义:
Budget: sched_runtime
Period: shced_period和deadline,一般来说这两个值是相等的。
EDF 的用法
Edf 提供出来的参数只有一个,这是路径如下:
/proc/sys/kernel/sched_dl_runtime_us
可以通过它来修改内核中的全局变量sysctl_sched_dl_runtime_us,它的作用就是用来设置edf调度类类队列中的所有实体能够占到cpu的比列。它的设置必须 和rt调度类的两个参数共同使用。
/proc/sys/kernel/sched_rt_period_us
/proc/sys/kernel/sched_rt_runtime_us
上面两个参数分别代表实时进程的周期和周期内能够运行的时间。
默认的情况下,这三个参数的值分别为:
Sched_dl_runtime_us = 400000
Sched_dl_period_us = 1000000
Sched_rt_runtime_us = 950000
那么如果机器上是四个cpu的话,edf的调度实体总共最大能占的cpu比例为:
100*4*950000/1000000*400000/1000000/100=152%
在sctual_dl_runtime的函数中会去计算这个值。
Edf提供的系统调用
__SYSCALL_I386(350,sys_sched_setparam2,sys_sched_setparam2)
__SYSCALL_I386(351,sys_sched_getparam2,sys_sched_getparam2)
__SYSCALL_I386(352,sys_sched_setscheduler2,sys_sched_setscheduler2)
4.3 EDF调度类实现
EDF和FIFO以及CFS一样,都是以调度类的方式实现调度算法。这样的结构是linux的一个特性。可以很好的将各种调度实现解耦。使其一个发生了问题,不会影响其它的调度。EDF的调度类在内核的位置如下所示:
这里写图片描述
图3 EDF调度类结构
EDF调度类
EDF的算法在linux中是以调度类形式存在的,其数据结构如下:
const struct sched_class dl_sched_class = {
.next = &rt_sched_class,
.enqueue_task = enqueue_task_dl,
.dequeue_task = dequeue_task_dl,
.yield_task = yield_task_dl,
.check_preempt_curr = check_preempt_curr_dl,
.pick_next_task = pick_next_task_dl,
.put_prev_task = put_prev_task_dl,

ifdef CONFIG_SMP

.select_task_rq     = select_task_rq_dl,
.set_cpus_allowed       = set_cpus_allowed_dl,
.rq_online             = rq_online_dl,
.rq_offline             = rq_offline_dl,
.pre_schedule       = pre_schedule_dl,
.post_schedule      = post_schedule_dl,
.task_woken     = task_woken_dl,

endif

.set_curr_task      = set_curr_task_dl,
.task_tick      = task_tick_dl,
.task_fork          = task_fork_dl,
.task_dead      = task_dead_dl,
.prio_changed           = prio_changed_dl,
.switched_from      = switched_from_dl,
.switched_to        = switched_to_dl,

};
从上面的数据结构可以看出,EDF的调度类在RT的前面(.next = &rt_sched_class),它的下面才是linux默认的实时调度类。当linux在调度的时候执行的函数如下:

pick_next_task(struct rq *rq)
{
……
for_each_class(class) {
p = class->pick_next_task(rq);
if (p)
return p;
……
}
上面的说明在系统调度的时刻,首先遍历调度类的链表,最左面的是最高的调度类。当这个rq上,也就是cpu上有最高的调度类,那首先从最高的调度类中查找在此队列上有没有任务,如果有就运行此任务。
enqueue_task_dl
将edf任务插入实时运行的队列中,此函数在中断启动后,就是一个实时任务执行完毕后,在等到周期减去运行的时间就是剩余的时间,edf的任务在一个周期内有两个定时器,一个是任务的运行的定时器,一个是运行后剩余时间的定时器。如下图所示:
这里写图片描述
图4
上图所示:定时器1是任务运行的定时器,当任务运行时,这是个定时器中

图16 定时器在EDF中的作用
中断产生,在中断里将任务放进运行对列,只有在运行队列里的任务才能得到cpu的调度。在此中断中就调用了enqueue_task_dl函数,在任务得到调度的时刻就会运行这个函数。定时器2是一个剩余时间的定时器。这个在后续讲解到。
dequeue_task_dl:将任务从运行队列中摘去,如果在可移出的列表中有这个任务,也将这个任务从可移出列表中删除。
yield_task_dl:当应用程序调用了sched_yield函数则调用此函数,作用就是将还没有运行完的runtime归零。更新当前的时间,然后退出运行队列。
check_preempt_curr_dl:判断如果当前的任务和唤醒的任务都是EDF任务的时候判断是否抢占。抢占的条件就是谁的绝对的deadline最小。时机是在一个任务插入运行对列和当一个任务由非EDF任务变成EDF任务的时候。
pick_next_task_dl:判断rq上dl队列是否有EDF任务,如果有则从红黑树中选择一个最小的deadline的任务。启动定时器。定时时间为当前的时间+剩余的时间。
put_prev_task_dl:更新当前任务的时间,如果在本cpu上还有其他的任务,将自己放到可以push的队列。以便在合适的时候在其他的cpu上运行。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值