在Linux中,调度器根据分配给每个线程或进程的”调度策略”和”调度优先级”控制执行顺序。
基础概念
静态优先级
静态优先级是在新进程创建时设置的默认优先级,不能更改。使用”实时策略”的进程优先级的值在1(最低)到99(最高)之间,被称为”实时优先级”。调度程序在一个自平衡的二叉搜索树中维护一个可运行进程列表。要确定哪些进程下一个获得CPU时间,调度器查找具有最高静态优先级或实时优先级的进程。调度策略只确定具有相同静态优先级的进程在运行队列中的顺序。使用非实时策略的进程不使用静态优先级,静态优先级设置为0。
动态优先级
当CPU不足时,进程优先级会被重置,因为优先级更高的进程会被优先调度。在此场景中,调度器提高CPU等待时间最长的进程的优先级。用户还可以使用nice或renice命令修改进程优先级。进程优先级可以在进程生命周期内增加或减少,这被称为动态优先级。
用于确定哪个非实时进程抢占其他非实时进程的值称为nice值。由于非实时进程的静态优先级设置为0,因此nice优先级值决定了非实时进程的相对调度。进程的权重控制进程获得的CPU数量,而nice值将衡量进程的权重。例如,在系统CPU饱和的情况下,nice值为10的进程获得的CPU时间是nice值为5的进程的两倍。当系统负载未达到CPU饱和时,每个进程将获得公平的CPU时间份额。nice的取值范围最高为-20,最低为19。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ps命令能列出进程的优先级 ps axo pid,pri,rtprio,ni,cls,comm PID PRI RTPRIO NI CLS COMMAND 1 19 - 0 TS systemd 2 19 - 0 TS kthreadd 3 39 - -20 TS rcu_gp 4 39 - -20 TS rcu_par_gp 5 39 - -20 TS slub_flushwq 7 39 - -20 TS kworker/0:0H-events_highpri 9 39 - -20 TS mm_percpu_wq 10 19 - 0 TS rcu_tasks_rude_ 11 19 - 0 TS rcu_tasks_trace
pri参数列出静态优先级 rtprio参数列出实时优先级 ni参数列出nice值 cls参数列出调度策略 - TS表示时间共享,non-real-time策略 - FF表示FIFO,real-time策略 cputime 占用的CPU时间
|
在任何给定的CPU上,在特定的时间只能运行一个进程。因此,进程在完成执行之前通常会停止和启动几次。默认情况下,内核分配一组处理时间给进程,称为时间片。
一个进程将它的时间片分成几个部分,而不是一次运行所有的部分。当某个进程的时间片耗尽,或者该进程正在等待某个事件时,该进程可以主动将CPU时间让给其他进程。当进程”非自愿”将CPU时间让给更高优先级的进程时,这称为”抢占“。抢占会损害应用程序的整体性能。
在进程之间切换时,内核存储前一个进程和下一个进程的信息。每次进程释放处理器时,内核都会
存储有关其当前操作状态的信息,以便 在再次调度它在处理器上运行时,它可以从相同的位置恢复
操作。这种操作状态数据称为上下文(context),它由CPU寄存器和程序计数器的内容组成。进
程对CPU时间的切换称为上下文切换(context switching)
改变进程的优先级可以帮助减少进程的上下文切换
调度策略分类
调度策略分为非实时策略组和实时策略组
实时调度策略
1 2 3 4 5
| 1.SCHED_FIFO “FF” 先进先出。直到IO阻塞或者被强占前都一直运行
2.SCHED_RR 轮询 round-robin 每个进程都将保持运行直到耗尽了提前分配的时间片,同优先级进程使用rr调度策略
|
这两种都是固定的实时优先级了,内核不会为实时任务计算动态优先级值。固定的实时优先级确保具有给定优先级的实时进程总是抢占较低优先级的进程。当系统过载时,会丢弃低优先级的任务
当使用SCHED_FIFO运行的高优先级进程时,系统的总体性能会降低。例如,执行密集计算的进程(例如对大矩阵求逆)永远不能使用SCHED_FIFO运行。
大多数内核线程使用SCHED_FIFO来避免用户级应用程序抢占。
非实时调度策略
1 2 3 4 5 6 7 8 9 10 11
| 1.SCHED_NORMAL(SCHED_OTHER) 该策略定义了round-robin风格的**时间共享**策略。该策略是Linux上大多数进程使用的**默认时间共 享策略。("TS")**
2.SCHED_BATCH 该策略有利于面向批处理的工作负载。它不像SCHED_NORMAL那样频繁抢占,因此任务运行时间更 长,可以更好地利用缓存。("B")
3.SCHED_IDLE 该策略有利于运行低优先级的应用程序。使用SCHED_IDLE运行的进程的优先级低于使用19的nice 值运行的进程。("IDLE")
|
除非特定应用需要使用固定优先级(fixed-priority)的调度策略,否则应用必须使用默认的调度策略。
调度器分类
RT实时调度器
实时调度类(RT Scheduler) 是内核中优先级仅次于 Deadline 调度类的高性能调度机制,专门用于处理对实时性要求极高的任务。它的核心目标是确保高优先级进程能够以最低延迟抢占 CPU,并提供确定性的响应时间。
- 管理策略:
- SCHED_FIFO(先进先出):高优先级进程独占 CPU,直到主动释放或更高优先级进程就绪。
- SCHED_RR(轮转调度):同优先级进程按时间片轮流运行。
CFS完全公平调度器
完全公平调度器(CFS)是在Linux kernel 2.6.23中引入的,是RHEL系统的默认调度器。
CFS使用SCHED_NORMAL(即SCHED_OTHER)调度策略,当调度上下文在系统生命周期内发生变化时,CFS将动态计算正在运行的进程的计算时间。
- 管理策略:
- SCHED_OTHER:普通进程,通过 nice 值调整权重。
- SCHED_BATCH:适用于非交互式批处理任务,减少调度开销。
- SCHED_IDLE:仅在系统空闲时运行,几乎不占用 CPU。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ls /proc/sys/kernel/sched_ sched_autogroup_enabled sched_energy_aware sched_rt_period_us sched_cfs_bandwidth_slice_us sched_latency_ns sched_rt_runtime_us sched_child_runs_first sched_migration_cost_ns sched_schedstats sched_deadline_period_max_us sched_min_granularity_ns sched_tunable_scaling sched_deadline_period_min_us sched_nr_migrate sched_wakeup_granularity_ns sched_domain/ sched_rr_timeslice_ms
sched_latency_ns 目标抢占延迟
sched_min_granularity_ns 定义进程在被抢占前的最短准备时间,必须小于sched_latency_ns 如果任务运行数量>sched_latency_ns/sched_min_granularity_ns
sched_migration_cost_ns 进程最后一次执行后,迁移到另一个CPU所需的事件 增加这个值可以减少跨CPU进程迁移
sched_rt_period_us 定义了一个时间窗口(周期),在此周期内实时任务的总运行时间被限制 默认值1s
sched_rt_runtime_us 定义在周期内,实时任务最多可以占用CPU的时间。默认0.95s 0:实时任务完全禁止运行 -1:取消实时任务的CPU时间限制(可能导致非实时任务被饿死)
默认占比:950000/1000000 = 95%,即实时任务最多占用95%的CPU时间 它们共同定义了实时任务在全局CPU时间配额中的限制规则,目的是防止实时任务完全独占CPU
sched_rr_timeslice_ms RR进程灾备抢占并放在CPU绑定队列末尾前,允许运行的时间片
|
Deadline调度器
实时系统必须在最后期限内响应请求
如果系统未能在deadline前作出响应,则认为系统未能交付结果。deadline调度程序根据流程截止日期调度任务,并优先调度截止日期最早的流程
deadline调度程序使用三个参数:周期、截止日期和运行时以纳秒为单位定义任务
1 2 3 4 5 6 7 8 9
| chrt -d --sched-runtime 5000000 --sched-deadline 10000000 \ --sched-period 1666666 0 simplte_task
chrt命令 --sched-runtime 定义运行时 --sched-deadline 定义相对截止时间 --sched-period 定义周期 单位为纳秒。-d选项使用SCHED_DEADLINE调度策略对任务进行调度。
|
为进程设置调度器与实时优先级
Linux提供了一系列用于管理调度器参数的系统调用,这些调用允许操作进程优先级、调度策略和处理器关联,从而将处理器交付给其他任务。
应用程序以编程方式使用sched_setscheduler()和sched_getscheduler()系统调用来设置和获取给定的调度策略和进程的实时优先级
chrt命令行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| chrt -p 1 pid 1's current scheduling policy: SCHED_OTHER pid 1's current scheduling priority: 0
-b:设置调度策略为SCHED_BATCH -f:设置调度策略为SCHED_FIFO -i:设置调度策略为SCHED_IDLE -o:设置调度策略为SCHED_NORMAL或者SCHED_OTHER -r:设置调度策略为SCHED_RR -d:设置调度策略为SCHED_DEADLI
chrt -f -p 90 5735 chrt -p 5735 pid 5735's current scheduling policy: SCHED_FIFO pid 5735's current scheduling priority: 90
chrt -o -p 0 5735 chrt -p 5735 pid 5735's current scheduling policy: SCHED_OTHER pid 5735's current scheduling priority: 0
sudo chrt -f 90 ./my_realtime_program sudo chrt -d --sched-runtime 10000000 \ --sched-deadline 20000000 \ --sched-period 30000000 \ ./my_deadline_program
|
tuna
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # 查看线程 tuna --show_threads thread ctxt_switches pid SCHED_ rtpri affinity voluntary nonvoluntary cmd 1 OTHER 0 0,1 28604 4275 systemd 2 OTHER 0 0,1 3926 0 kthreadd 3 OTHER 0 0,1 2 0 rcu_gp 4 OTHER 0 0,1 2 0 rcu_par_gp 5 OTHER 0 0,1 2 0 slub_flushwq 7 OTHER 0 0 4 0 kworker/0:0H-events_highpri 9 OTHER 0 0,1 200038 19 kworker/u4:0-events_unbound
# 将 PID 1000 的调度策略改为 SCHED_FIFO,优先级 90 sudo tuna --threads=1000 --sched=FIFO --priority=90 --run
# 将进程名包含 "realtime" 的线程设为 SCHED_RR,优先级 80 sudo tuna --threads="*realtime*" --sched=RR --priority=80 --run
|
systemd
- **CPUSchedulingPolicy=**:设置调度策略(可选值:other/batch/idle/fifo/rr)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| [Service]
CPUSchedulingPolicy=fifo CPUSchedulingPriority=90
CPUAffinity=0 1
Nice=0
systemctl daemon-reload systemctl restart xxx
|