Linux调度和抢占

Linux调度和抢占

任务种类

• 普通任务(2.6版本以后分为交互式任务和批处理任务)
• 实时任务

实时任务和非实时任务

• 实时任务在同优先级下支持两种调度策略,FIFO(先进先出)和RR(时间片轮转)。
• FIFO模式下先进任务队列的任务先运行,先进的运行完才会运行后进的,只有更高优先级的和RR任务到来时,才能抢占现有的任务。
• RR模式采用时间片轮转。任务在耗尽它的时间片时,在同一优先级的其他实时进程轮流调度,不同优先级下,高优先级的总是先运行,且低优先级的FIFO进程不能抢占RR进程。
• 非实时任务(普通任务)采用时间片轮转调度,但是时间片的大小会根据优先级的不同调整
前两者属于普通任务,后者属于实时任务

静态优先级和动态优先级

静态优先级与nice值相关,进程创建的时候就分配,静态优先级决定了任务基础的时间片大小,优先级越高时间片越大
动态优先级是根据任务执行情况动态分配的,有一个公式计算

动态优先级 = max(100 , min(静态优先级 – bonus + 5 ,139))

其中调整的系数bonus为一个惩戒值,这个如果一个任务被界定为交互型任务,那么奖励值就高,反之就是CPU消耗型任务,奖励值就低。
影响bonus的因素有很多,包括cpu上的执行时间、在runqueue中的等待时间、睡眠时间、睡眠时候的进程状态(是否可被信号打断),什么上下文唤醒时间等,这样可以更好的区分交互任务还是批处理任务,一般来说交互型任务平均睡眠实际都比较长。
普通非实时任务采用动态优先级调度
实时任务优先级在创建时确定,不采用动态优先级,所以采用静态优先级调度

任务抢占

产生时机(产生need_resched标志)

• 周期性时钟中断
• 唤醒进程或者新建进程
• 带宽控制
• 优先级切换等

发生时机

用户任务:
• 系统调用返回
• 中断返回
内核任务:
• 中断返回内核态
• 开启抢占
• 手动调用schedule();
• 任务阻塞

用户抢占

内核返回用户空间时,如果need_resched被置位,就会导致schedule()被调用,就会发生抢占

内核抢占

内核任务只要没有加锁的部分都支持抢占,每个进程的thread_info引入了preempt_count计数器,初始值为0,加锁的时候加1,释放锁的时候减1,当数值为0的时候就可以执行抢占
中断返回内核空间时,内核会同时检查need_resched和preempt_count,两者都符合条件,就执行抢占,如果preemt_count不为0 ,那么先执行中断返回,等preemt_count为0时,会再次触发检查need_resched标志位,如果是的话,会立刻调用调度程序

调度器

5个调度器类

而依据其调度策略的不同实现了5个调度器类, 一个调度器类可以用一种种或者多种调度策略调度某一类进程, 也可以用于特殊情况或者调度特殊功能的进程,按照优先级从高到低分别是:
• stop_sched_class
• dl_sched_class :deadline调度器
• rt_sched_class:实时调度器
• fair_sched_class :完全公平调度器
• idle_sched_class:空闲调度器
Linux调度核心在选择下一个合适的task运行的时候,会按照优先级的顺序遍历调度类的pick_next_task函数。因此,SCHED_FIFO调度策略的实时进程永远比SCHED_NORMAL调度策略的普通进程优先运行

6种调度策略

linux内核目前实现了6中调度策略(即调度算法), 用于对不同类型的进程进行调度, 或者支持某些特殊的功能
SCHED_NORMAL和SCHED_BATCH调度普通的非实时进程
SCHED_FIFO和SCHED_RR和SCHED_DEADLINE则采用不同的调度策略调度实时进程
SCHED_IDLE则在系统空闲时调用idle进程

Q1调度器

时间复杂度是O(1),根据每个任务的静态优先级先分配基础的时间片,然后根据动态优先级动态确定调度策略。
它有一张Bitmap表,描述了所有的进程优先级,每个元素指向了某个固定优先级的任务队列,每个任务优先级有两个队列,一个是active 一个是expired , 当actve列表里的任务时间片用完,就会被丢到expired队列里,如果整个active队列用完,会直接和expired互换,从而进行下一次任务调度。
如果有交互型需求高的任务时间片用完,不会立刻丢到expired队列中,而是会重新分配一个时间片然后继续丢到active队列中,如果expired队列饥饿等待时间过长,那么会强制切换到active队列中
缺点就是时间粒度过大,由于任务优先级高的任务时间片就大,当交互型任务被分配到更多的时间片时,其实他们是用不完的,就造成资源的浪费。

deadline调度器

deadline调度器有三个参数:周期(period)、运行时间(runtime)和最后期限(deadline)。周期和该实时任务的工作模式相关。
• 周期:表示这个任务执行的周期
• 运行时间:表示一个周期内,需要占用CPU的时间
• 最后期限:规定了一个周期内,任务完成的最后期限
调度原理:deadline最早到来的那个任务最先调度执行,对于有N个处理器的系统,优先级最高的前N个deadline任务(即deadline最早到来的前N个任务)将被选择在对应N个处理器上运行。调度器可以保证每个任务在一个周期内可以获得完整的运行时间。如果运行时间使用完毕,任务就会被踢出调度器,等到下个周期到来,重新补充运行时间时才会重新被调度。
为了避免DL任务过多造成系统超负荷运行,调度器有个准入机制,在任务配置好上述三个调度参数并准备加入到调度器时,调度器会对该任务进行评估,这个举动确保了系统不会超负荷运作。

CFS调度器

CFS调度器允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下个运行进程。CFS在所有可运行进程总数基础上计算出一个进程应该运行的比重,而不是只依靠nice值。nice值越高,优先级越低,权重越低
当CFS需要选择下一个运行进程时,它会挑选一个具有最小运行时间(已经运行的时间)的进程,由于CFS用红黑树来组织可运行进程队列,所以可以很快找到最小运行时间值的进程
调度延迟:调度延迟是内核中的固有概念(不是固定值),他表示了一段时间,并且在这段时间内,所有的可被调度程序都应该至少被运行一次
CFS调度器的调度延迟时间的设定并不是固定的:
• 当系统处于就绪态的进程少于一个定值sched_nr_latency(默认值8)的时候,调度延迟也是固定一个值不变sched_latency_ns(6ms或者12ms)。
• 当系统就绪态进程个数超过sched_nr_latency,我们保证每个进程至少运行一次,也就是sched_min_granularity_ns(最小粒度时间,默认为0.75ms或者1.5ms)*进程数才让出cpu。
• sched_latency_ns和sched_min_granularity_ns都在/proc/sys/kernel/路径下可以查看,shced_nr_latency则通过前面两个值计算得到,公式为sched_latency_ns/sched_min_granularity_ns
其他参数
• sched_wakeup_granularity_ns:该变量表示进程被唤醒后至少应该运行的时间的基数,它只是用来判断某个进程是否应该抢占当前进程,并不代表它能够执行的最小时间
• sched_child_runs_first(默认0):该变量表示在创建子进程的时候是否让子进程抢占父进程,即使父进程的vruntime小于子进程,这个会减少公平性,但是可以降低write_on_copy,具体要根据系统的应用情况来考量使用哪种方式
• sched_cfs_bandwidth_slice_us(默认5ms):该变量表示CFS调度器申请cpu时间时,单次申请的份额
• sched_rt_period_us(默认1s):调度实时进程的周期,
• sched_rt_runtime_us(默认0.95秒): 表示一个调度周期内,所有实时进程一次能占有CPU的最长时间,缺省是0.95秒,也就是说,默认情况下,实时进程占有不超过95%的CPU

3个调度实体

调度器不限于调度进程, 还可以调度更大的实体, 比如实现组调度.
这种一般性要求调度器不直接操作进程, 而是处理可调度实体, 因此需要一个通用的数据结构描述这个调度实体,即seched_entity结构, 其实际上就代表了一个调度对象,可以为一个进程,也可以为一个进程组.
linux中针对当前可调度的实时和非实时进程, 定义了类型为seched_entity的3个调度实体
sched_dl_entity 采用EDF算法调度的实时调度实体
sched_rt_entity 采用Roound-Robin或者FIFO算法调度的实时调度实体 rt_sched_class
sched_entity 采用CFS算法调度的普通非实时进程的调度实体
调度器整体框架
每个进程都属于某个调度器类(由字段task_struct->sched_class标识), 由调度器类采用进程对应的调度策略调度(由task_struct->policy )进行调度, task_struct也存储了其对应的调度实体标识

linux实现了6种调度策略, 依据其调度策略的不同实现了5个调度器类, 一个调度器类可以用一种或者多种调度策略调度某一类进程, 也可以用于特殊情况或者调度特殊功能的进程。

task_struct采用了四个成员表示进程的优先级:prio和normal_prio表示动态优先级, static_prio表示进程的静态优先级. 同时还用了rt_priority表示实时进程的优先级

内核通过normal_prio函数计算普通优先级normal_prio 通过effective_prio函数计算动态优先级prio

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值