作者 | 时间 | QQ技术交流群 |
---|---|---|
perrynzhou@gmail.com | 2022/05/22 | 672152841 |
进程优先级
Linux内核中进程优先级一般分为动态优先级和静态优先级,动态优先级是内核根据进程的nice值、IO密集行为或者计算密集行为以及等待时间等因素,设置给普通的进程;静态优先级是用户态应用设置给实时进程。在调度中静态优先级的进程优先级更高。
一般应用分为IO密集型和计算密集型;
I/O密集型
是进程执行I/O操作时候等待资源或者事件时候,数据读取到后恢复进程的运行,这样基本出于等待IO和运行之间进行交替,由于具有这样的特性,进程调度器通常会将短的CPU时间片分配给I/O密集型进程。计算密集型
是进程保持在CPU时间片上,需要最大限度的利用处理器的计算能力。任何普通进程
的nice值在-20~19
之间,0
是默认,较高的nice值表示较低的进程优先级;实时进程
的优先级范围是在0~99
从内核的优先级角度,所有的进程都处于
0~139
,其中的0~99
是分配实时进程;100~139
是分配普通进程,代表的nice值为-20~19
.
实际调度器
调度器通用元素
CFS(完全公平)调度器
Linux内核中所有动态优先级的进程都是有
CFS
调度器处理,通常Linux内核中大部分都是非实时进程,所以CFS
进程调度器也是最繁忙的调度器。CFS
调度器不依赖于传统的时间片来分配CPU的时间,而是通过虚拟时间,这个虚拟时间是进程获取CPU时间的时间单位。CFS
调度器根据虚拟时间值,CFS
决定进程的最终运行时间,同时也会涉及使用nice来衡量一个进程和其他进程的竞争关系。优先级是根据进程等待时间、进程运行时间、进程的历史行为、进程的nice值等因素动态设置。在计算进程优先级的时候,
CFS
不仅依赖于进程的nice值,同时还要考虑进程的负载,内核中维护了进程的负载权重数组(prio_to_weight
),每个nice值对应一个负载权重;对于进程来说,nice值减一,CPU时间片就会减少10%;nice值加一,CPU时间片会增加10%。CFS
调度器采用了红黑树来作为运行队列,红黑树保存了所有的竞争进程,根据红黑树的特性,执行插入和搜索效率非常高。高优先级的进程在树的最左边节点,通过pick_next_task()
从红黑树中选择最左边的节点来进行调度。
// CFS完全公平调度器
const struct sched_class fair_sched_class = {
.next = &idle_sched_class,
.enqueue_task = enqueue_task_fair,
.dequeue_task = dequeue_task_fair,
.yield_task = yield_task_fair,
.yield_to_task = yield_to_task_fair,
.check_preempt_curr = check_preempt_wakeup,
.pick_next_task = pick_next_task_fair,
.put_prev_task = put_prev_task_fair,
#ifdef CONFIG_SMP
.select_task_rq = select_task_rq_fair,
.migrate_task_rq = migrate_task_rq_fair,
.rq_online = rq_online_fair,
.rq_offline = rq_offline_fair,
.task_dead = task_dead_fair,
.set_cpus_allowed = set_cpus_allowed_common,
#endif
.set_curr_task = set_curr_task_fair,
.task_tick = task_tick_fair,
.task_fork = task_fork_fair,
.prio_changed = prio_changed_fair,
.switched_from = switched_from_fair,
.switched_to = switched_to_fair,
.get_rr_interval = get_rr_interval_fair,
.update_curr = update_curr_fair,
#ifdef CONFIG_FAIR_GROUP_SCHED
.task_change_group = task_change_group_fair,
#endif
};
实时调度器
Linux内核中支持实时进程,它们是由实时调度器来进行调度。
rt进程
被分配了静态优先级,并且在内核中保持不变。于CFS
不同,实时调度器
采用了每个优先级1~99
的单链表,并非采红黑树作为运行队列。实时调度器
采用了fifo
和rr
调度策略.fifo
调度策略采用先进先出的方法来调度实时进程。在该策略下进程是没有任何时间片下运行的,一直在运行。当调度器遇到优先级更高的可执行的fifo
、rr
、deadline
任务时候,fifo
进程会被抢占;rr
调度策略是采用轮询的方法来调度实时进程,这个策略和fifo
类型类似,不同的是rr
调度策略是给进程分配了时间片来运行。进程的时间片过期后,进程会被添加到链表的尾部,具有相同优先级的进程会以轮询的方式运行,直到高优先级进程抢占。
// fifo和rt的实时进程调度器
const struct sched_class rt_sched_class = {
.next = &fair_sched_class,
.enqueue_task = enqueue_task_rt,
.dequeue_task = dequeue_task_rt,
.yield_task = yield_task_rt,
.check_preempt_curr = check_preempt_curr_rt,
.pick_next_task = pick_next_task_rt,
.put_prev_task = put_prev_task_rt,
#ifdef CONFIG_SMP
.select_task_rq = select_task_rq_rt,
.set_cpus_allowed = set_cpus_allowed_common,
.rq_online = rq_online_rt,
.rq_offline = rq_offline_rt,
.task_woken = task_woken_rt,
.switched_from = switched_from_rt,
#endif
.set_curr_task = set_curr_task_rt,
.task_tick = task_tick_rt,
.get_rr_interval = get_rr_interval_rt,
.prio_changed = prio_changed_rt,
.switched_to = switched_to_rt,
.update_curr = update_curr_rt,
};
deadline
调度策略,在Linux内核的3.14开始引入了,deadline
调度器基于全局最早的截止期优先和固定带宽服务器算法,于预先确定其运行时的需求。一个进程内运行多个任务,每个任务都会有一个截止期(在截止期内必须完成执行)和一个计算时间,该时间定义ICPU完成进程执行所需要的时间。
// 实时进程的新的调度器deadline
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,
.migrate_task_rq = migrate_task_rq_dl,
.set_cpus_allowed = set_cpus_allowed_dl,
.rq_online = rq_online_dl,
.rq_offline = rq_offline_dl,
.task_woken = task_woken_dl,
#endif
.set_curr_task = set_curr_task_dl,
.task_tick = task_tick_dl,
.task_fork = task_fork_dl,
.prio_changed = prio_changed_dl,
.switched_from = switched_from_dl,
.switched_to = switched_to_dl,
.update_curr = update_curr_dl,
};