4. 调度周期与最小粒度:三个核心参数的默认值与作用

CFS 调度器的三个核心参数——sysctl_sched_latency、sysctl_sched_min_granularity 和 sysctl_sched_wakeup_granularity——共同决定了调度周期、进程运行的最小时间片以及唤醒抢占的触发条件。理解它们的默认值、作用机制和相互约束关系,是进行 CFS 调优的第一步。

4.1 参数总览与默认值

以下表格列出了三个参数在 Linux 5.x / 6.x 内核中的默认值及其核心语义:

参数名 默认值 单位 核心语义
sysctl_sched_latency 6,000,000 (6 ms) ns (纳秒) CFS 调度周期的目标时长,即所有可运行进程在一个完整调度周期内至少被调度一次的时间窗口。
sysctl_sched_min_granularity 750,000 (0.75 ms) ns (纳秒) 每个进程获得的最小运行时间片,防止进程切换过于频繁导致上下文切换开销过大。
sysctl_sched_wakeup_granularity 1,000,000 (1 ms) ns (纳秒) 唤醒进程抢占当前运行进程所需的虚拟时间差阈值,用于控制唤醒抢占的激进程度。
内核版本差异提示: 默认值可能因内核版本和架构(如 NUMA 或 SMP)而略有不同。例如,在服务器内核中,sysctl_sched_latency 可能被调整为 12 ms 或更高。生产环境中应通过 cat /proc/sys/kernel/sched_latency_ns 确认当前值。

4.2 调度周期(sysctl_sched_latency)的作用机制

sysctl_sched_latency 是 CFS 调度器的“时间预算”。它定义了 CFS 在多长时间内保证所有可运行进程至少获得一次 CPU 执行机会。CFS 根据这个周期和当前可运行进程的数量,动态计算每个进程的时间片:

进程时间片 = sysctl_sched_latency / 可运行进程数

当可运行进程数较少时,每个进程获得的时间片较大,调度开销占比低;当进程数增多时,时间片自动缩小,但不会低于 sysctl_sched_min_granularity 的约束。

关键约束: 如果 sysctl_sched_latency / nr_running < sysctl_sched_min_granularity,则实际时间片会被钳位在 sysctl_sched_min_granularity,此时调度周期会相应延长为 sysctl_sched_min_granularity * nr_running。

下面的 SVG 图展示了调度周期与进程时间片的动态分配关系:

调度周期与时间片动态分配 调度周期 (sysctl_sched_latency = 6 ms) 进程 A 时间片 进程 B 时间片 进程 C 时间片 进程 D 时间片 计算公式: 时间片 = sysctl_sched_latency / nr_running 当 nr_running = 4 时:时间片 = 6 ms / 4 = 1.5 ms

4.3 最小粒度(sysctl_sched_min_granularity)的保护作用

sysctl_sched_min_granularity 是 CFS 调度器的“安全垫”。它确保每个进程至少运行一个最小时间片,避免在进程数量过多时,时间片被切割得过于细碎,导致上下文切换开销(TLB 刷新、缓存污染)吞噬有效计算时间。

  • 默认值 0.75 ms:在桌面和通用服务器场景下,这是一个平衡点。低于此值,切换开销占比会显著上升。
  • 约束优先级:当计算出的时间片小于此值时,CFS 会强制使用 sysctl_sched_min_granularity 作为实际时间片,并相应延长调度周期。
  • 调优方向:对于延迟敏感型负载(如实时音频),可适当减小此值以换取更细的调度粒度;对于吞吐优先型负载(如批处理),可增大此值以减少切换次数。
调优陷阱: 将 sysctl_sched_min_granularity 设置得过小(如 < 0.2 ms)会导致上下文切换开销急剧增加,在进程数超过 10 个时,CPU 可能花费超过 20% 的时间在切换上,而非执行用户代码。

4.4 唤醒粒度(sysctl_sched_wakeup_granularity)的抢占控制

sysctl_sched_wakeup_granularity 决定了当进程从睡眠状态被唤醒时,能否立即抢占当前正在运行的进程。CFS 通过比较唤醒进程与当前进程的虚拟时间(vruntime)差值来判断:

if (唤醒进程.vruntime + sysctl_sched_wakeup_granularity < 当前进程.vruntime)
    触发抢占;
else
    等待当前进程时间片用完;

这个参数的本质是“抢占阈值”。值越小,唤醒进程越容易抢占当前进程,交互响应越快,但可能导致频繁的上下文切换和缓存抖动。值越大,唤醒进程越倾向于等待,系统吞吐更稳定,但交互延迟可能增加。

下面的 SVG 图展示了唤醒抢占的判断逻辑:

唤醒抢占判断逻辑 当前进程 (vruntime=100) 唤醒进程 (vruntime=80) vruntime + wakeup_granularity 80 + 1 ms < 100 ? 是 → 立即抢占 否 → 等待时间片

4.5 三个参数的协同关系

这三个参数并非独立工作,而是形成一个相互约束的三角关系:

  1. 调度周期(latency) 是顶层时间预算,决定了公平性的时间窗口。
  2. 最小粒度(min_granularity) 是底层保护,防止时间片过小导致切换开销失控。
  3. 唤醒粒度(wakeup_granularity) 是抢占调节器,在交互响应和系统吞吐之间做权衡。

当 sysctl_sched_latency 被调大时,每个进程的时间片会增大,但调度周期变长,可能导致新唤醒的进程等待更久才能获得 CPU。此时,适当调小 sysctl_sched_wakeup_granularity 可以补偿交互延迟。反之,如果调小了 sysctl_sched_latency,时间片变短,可能需要同步调大 sysctl_sched_min_granularity 来防止切换过频。

生产环境经验: 在数据库服务器(如 MySQL、PostgreSQL)中,通常将 sysctl_sched_latency 从 6 ms 增大到 12-24 ms,同时将 sysctl_sched_min_granularity 从 0.75 ms 增大到 1.5-3 ms,以减少上下文切换次数,提升缓存命中率。对于 Web 服务器(如 Nginx),则倾向于保持默认值或略微减小 sysctl_sched_wakeup_granularity 以改善请求响应速度。

4.6 查看与临时修改

在 Linux 系统中,可以通过 /proc 文件系统实时查看和修改这些参数:

# 查看当前值
cat /proc/sys/kernel/sched_latency_ns
cat /proc/sys/kernel/sched_min_granularity_ns
cat /proc/sys/kernel/sched_wakeup_granularity_ns

# 临时修改(重启后失效)
echo 12000000 > /proc/sys/kernel/sched_latency_ns
echo 1500000 > /proc/sys/kernel/sched_min_granularity_ns
echo 500000 > /proc/sys/kernel/sched_wakeup_granularity_ns
持久化建议: 将修改写入 /etc/sysctl.conf 或 /etc/sysctl.d/99-cfs-tuning.conf,格式为 kernel.sched_latency_ns=12000000,然后执行 sysctl -p 生效。