Linux 内核调度器原理与调优详解
·
Linux 内核调度器原理与调优详解
调度器概述
Linux内核调度器是操作系统的核心组件之一,负责决定哪个进程获得CPU时间片,以及何时获得。调度器的设计直接影响系统的性能、响应速度和资源利用率。了解调度器的原理和调优方法对于优化系统性能至关重要。
调度器的作用
- 资源分配:合理分配CPU资源给各个进程
- 公平性:确保每个进程都能获得合理的CPU时间
- 响应性:保证交互式进程的响应速度
- 吞吐量:最大化系统的整体吞吐量
- 实时性:满足实时进程的时间要求
调度器的挑战
- 多任务处理:同时处理多个进程的调度
- 优先级管理:处理不同优先级的进程
- 负载均衡:在多CPU系统中平衡负载
- 能耗优化:在保证性能的同时减少能耗
- 公平性与效率:在公平性和系统效率之间取得平衡
调度器的基本概念
进程状态
- 运行态(R):进程正在CPU上执行
- 就绪态(R):进程已准备好执行,等待CPU时间
- 睡眠态(S/D):进程等待某个事件,不占用CPU
- 停止态(T):进程被暂停执行
- 僵尸态(Z):进程已终止,但父进程未回收
调度策略
- SCHED_NORMAL:普通进程调度策略,使用CFS调度器
- SCHED_FIFO:先进先出实时调度策略
- SCHED_RR:时间片轮转实时调度策略
- SCHED_DEADLINE:截止时间调度策略
优先级
- 静态优先级:进程的基本优先级,范围为0-139
- 动态优先级:考虑进程的交互性后计算的优先级
- 实时优先级:实时进程的优先级,范围为0-99
时间片
- 时间片长度:进程每次获得CPU的时间长度
- 时间片分配:根据进程优先级和交互性分配时间片
- 时间片耗尽:进程时间片用完后重新调度
Linux调度器的演进
早期调度器
- O(1)调度器:Linux 2.6引入,使用红黑树管理进程,调度时间复杂度为O(1)
- 轮转调度器:早期的时间片轮转调度器
- 优先级调度器:基于优先级的调度器
CFS调度器
- 引入时间:Linux 2.6.23引入
- 设计理念:完全公平调度,基于虚拟运行时间
- 实现机制:使用红黑树管理进程,按虚拟运行时间排序
- 特点:公平性好,适合交互式系统
实时调度器
- SCHED_FIFO:先进先出调度,适用于对延迟敏感的实时任务
- SCHED_RR:时间片轮转调度,适用于需要公平性的实时任务
- SCHED_DEADLINE:截止时间调度,适用于有严格时间要求的任务
CFS调度器原理
设计理念
CFS(Completely Fair Scheduler)的设计理念是:每个进程都应该获得与其权重成比例的CPU时间。CFS通过以下机制实现公平调度:
- 虚拟运行时间:记录进程的实际运行时间,按权重缩放
- 红黑树:按虚拟运行时间排序进程,选择虚拟运行时间最小的进程执行
- 调度实体:将进程抽象为调度实体,支持组调度
核心数据结构
// 调度实体结构
struct sched_entity {
struct load_weight load; // 进程的权重
struct rb_node run_node; // 红黑树节点
unsigned int on_rq; // 是否在运行队列中
u64 exec_start; // 执行开始时间
u64 sum_exec_runtime; // 累计执行时间
u64 vruntime; // 虚拟运行时间
// 其他字段...
};
// 运行队列结构
struct cfs_rq {
struct rb_root tasks_timeline; // 进程红黑树
struct rb_node *rb_leftmost; // 最左侧节点(虚拟运行时间最小)
unsigned int nr_running; // 运行队列中的进程数
u64 min_vruntime; // 最小虚拟运行时间
// 其他字段...
};
调度过程
- 进程创建:为进程创建调度实体,初始化权重和虚拟运行时间
- 进程入队:将进程添加到运行队列的红黑树中
- 选择下一个进程:选择虚拟运行时间最小的进程执行
- 时间片计算:根据进程权重计算时间片
- 进程出队:进程时间片用完后移出运行队列
- 负载均衡:在多CPU系统中平衡负载
虚拟运行时间计算
// 虚拟运行时间计算
vruntime = actual_runtime * NICE_0_LOAD / weight;
// 其中:
// actual_runtime:实际运行时间
// NICE_0_LOAD:nice值为0的进程权重
// weight:进程的实际权重
优先级调整
CFS通过nice值调整进程的权重:
- nice值范围:-20到19
- 权重计算:nice值越高,权重越低
- CPU时间分配:权重越高,获得的CPU时间越多
实时调度器
SCHED_FIFO
- 工作原理:先进先出,一旦进程获得CPU,就会一直执行直到主动放弃或被更高优先级的进程抢占
- 适用场景:对延迟敏感的实时任务,如工业控制、音频处理
- 特点:简单高效,但可能导致低优先级进程饥饿
SCHED_RR
- 工作原理:时间片轮转,每个进程有一个时间片,时间片用完后重新排队
- 适用场景:需要公平性的实时任务
- 特点:兼顾公平性和实时性
SCHED_DEADLINE
- 工作原理:基于截止时间的调度,选择截止时间最早的任务执行
- 适用场景:有严格时间要求的实时任务,如视频编码
- 特点:能够保证任务在截止时间前完成
调度器的实现机制
调度时机
- 时间片耗尽:进程时间片用完时
- 进程状态变化:进程从运行态变为睡眠态时
- 进程创建:新进程创建时
- 优先级变化:进程优先级发生变化时
- 系统调用:进程调用sched_yield()主动放弃CPU时
上下文切换
- 保存上下文:保存当前进程的寄存器状态
- 切换页表:切换到新进程的页表
- 恢复上下文:恢复新进程的寄存器状态
- 更新调度信息:更新调度统计信息
负载均衡
- 触发时机:定期触发或当CPU负载不均衡时
- 负载计算:计算每个CPU的负载
- 进程迁移:将进程从负载高的CPU迁移到负载低的CPU
- 缓存考虑:考虑缓存亲和性,减少缓存失效
组调度
- 设计理念:将进程分组,对组进行公平调度
- 实现机制:使用层次化的调度实体
- 适用场景:容器、虚拟化环境
调度器调优
系统级调优
内核参数
# 调整进程调度策略
# 临时设置
sysctl -w kernel.sched_autogroup_enabled=1
# 永久设置
# 在/etc/sysctl.conf中添加
kernel.sched_autogroup_enabled=1
# 调整调度延迟
sysctl -w kernel.sched_latency_ns=10000000
sysctl -w kernel.sched_min_granularity_ns=1000000
sysctl -w kernel.sched_wakeup_granularity_ns=1500000
调度器选择
- 桌面系统:使用CFS调度器,注重交互式性能
- 服务器系统:使用CFS调度器,注重吞吐量
- 实时系统:使用实时调度器,注重实时性
进程级调优
优先级调整
# 调整进程优先级
nice -n 10 ./application
# 调整实时优先级
chrt -f -p 99 PID
# 查看进程优先级
ps -o pid,ni,pri,cmd
调度策略设置
#include <sched.h>
// 设置实时调度策略
struct sched_param param;
param.sched_priority = 99;
sched_setscheduler(0, SCHED_FIFO, ¶m);
// 设置普通进程优先级
nice(10);
多CPU系统调优
亲和性设置
# 设置进程亲和性
taskset -c 0,1 ./application
# 查看进程亲和性
taskset -p PID
#include <sched.h>
// 设置进程亲和性
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(0, &mask);
CPU_SET(1, &mask);
sched_setaffinity(0, sizeof(mask), &mask);
负载均衡调优
# 调整负载均衡参数
sysctl -w kernel.sched_migration_cost_ns=500000
sysctl -w kernel.sched_nr_migrate=32
实时性能调优
内核配置
- 启用实时补丁:应用PREEMPT_RT补丁
- 配置内核:选择合适的抢占模式
- 禁用不必要的功能:减少内核开销
系统配置
- 调整中断处理:优化中断处理 latency
- 禁用CPU频率缩放:保持CPU频率稳定
- 调整内存管理:减少内存分配延迟
- 优化I/O调度:选择合适的I/O调度器
应用程序优化
- 使用实时调度策略:为关键任务设置实时优先级
- 减少系统调用:减少用户态和内核态切换
- 优化代码:减少执行时间和抖动
- 使用无锁数据结构:减少锁竞争
监控与分析
调度器监控工具
- schedtool:查看和修改进程调度参数
- chrt:查看和修改进程实时调度参数
- taskset:查看和修改进程亲和性
- top:查看进程CPU使用情况
- htop:交互式进程查看器
- pidstat:查看进程统计信息
调度器分析工具
- perf:性能分析工具,可分析调度事件
- ftrace:跟踪内核函数调用,包括调度器
- trace-cmd:ftrace的前端工具
- kernelshark:图形化的ftrace分析工具
分析示例
# 使用perf分析调度事件
perf record -e sched:* -a sleep 10
perf report
# 使用ftrace跟踪调度器
cd /sys/kernel/debug/tracing
echo 1 > events/sched/enable
echo 1 > tracing_on
# 运行应用程序
cat trace
实际案例分析
桌面系统调优
需求:优化桌面系统的响应速度和流畅度
调优策略:
- 启用自动分组调度:
sysctl -w kernel.sched_autogroup_enabled=1 - 调整调度延迟:
sysctl -w kernel.sched_latency_ns=10000000 - 为交互式应用设置较高优先级:
nice -n -5 ./application - 禁用不必要的后台进程:减少系统负载
服务器系统调优
需求:优化服务器系统的吞吐量和稳定性
调优策略:
- 禁用自动分组调度:
sysctl -w kernel.sched_autogroup_enabled=0 - 调整调度粒度:
sysctl -w kernel.sched_min_granularity_ns=2000000 - 为批处理任务设置较低优先级:
nice -n 10 ./application - 配置CPU亲和性:将不同服务绑定到不同CPU
实时系统调优
需求:优化实时系统的实时性能和确定性
调优策略:
- 应用PREEMPT_RT补丁:获得硬实时能力
- 为实时任务设置实时调度策略:
chrt -f -p 99 PID - 调整中断处理:
sysctl -w kernel.sched_rt_runtime_us=-1 - 禁用CPU频率缩放:
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
最佳实践
系统级最佳实践
- 根据 workload 选择调度器:不同的工作负载需要不同的调度策略
- 合理设置内核参数:根据系统特点调整调度器参数
- 监控系统负载:及时发现和解决负载不均衡问题
- 定期优化:根据系统运行情况定期调整配置
应用级最佳实践
- 合理设置进程优先级:根据进程的重要性设置合适的优先级
- 使用适当的调度策略:为不同类型的任务选择合适的调度策略
- 优化进程行为:减少进程的调度开销
- 避免长时间占用CPU:定期主动放弃CPU,给其他进程执行机会
多CPU系统最佳实践
- 合理设置CPU亲和性:将进程绑定到特定CPU,减少缓存失效
- 避免过度并行:根据CPU核心数合理设置并行度
- 负载均衡:确保各CPU负载均衡
- 考虑NUMA架构:在NUMA系统中,尽量将进程和其使用的内存放在同一节点
总结
Linux内核调度器是操作系统的核心组件,负责合理分配CPU资源给各个进程。了解调度器的原理和调优方法对于优化系统性能至关重要。
CFS调度器是Linux的默认调度器,采用完全公平的设计理念,适合大多数场景。实时调度器则适用于对实时性要求较高的场景。
通过系统级和进程级的调优,可以显著提高系统的性能和响应速度。监控和分析工具可以帮助我们了解调度器的行为,发现和解决调度问题。
不同类型的系统需要不同的调度策略和调优方法,开发者和系统管理员需要根据具体的应用场景和系统特点,选择合适的调度策略和调优参数。
随着硬件的发展和应用需求的变化,Linux调度器也在不断演进,以适应新的挑战。持续学习和掌握最新的调度器技术,是提高系统性能的关键。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)