人人都会AI编程

21.1 CPU 调度优化:进程绑定、调度策略调整、中断均衡

更新时间:2026-07-12

在多核系统上,CPU 资源如何被分配,直接决定了关键业务的吞吐量和延迟。本节介绍的三个优化方向——进程绑定到特定核心、调整调度策略以及均衡硬件中断——并不需要修改内核代码,只需利用内核现有的接口就能实现显著的性能提升。

1. 进程绑定:让关键任务独占或固定在某组 CPU 核心上

默认情况下,Linux 调度器会在所有可用核心之间迁移进程,以尽量让各核心的任务队列长度接近。但这种迁移会带来缓存失效和上下文切换的开销。对于数据库、高性能计算或实时任务,显式地将进程“钉”在指定的核心上,既能避免迁移损耗,也能隔离干扰。

查看进程当前绑定的核心

taskset -cp <PID>            # 查看进程的 CPU 亲和性,以核心列表和掩码形式展示
cat /proc/<PID>/status | grep Cpus_allowed_list

启动时指定绑核

taskset -c 0,2,4 ./my_server    # 将进程限定在 core0、2、4 上运行

运行时调整现有进程的绑核

taskset -cp 3-7 <PID>          # 将已在运行的进程重新绑定到 core3~core7

更精细的控制:cgroup cpuset
当需要为多个进程组成的服务设置独立的 CPU 资源池时,cgroupcpuset 子系统更合适。它不仅可以限定可用的核心,还能指定内存节点。

# 创建一个 cgroup,分配核心 4-7
mkdir /sys/fs/cgroup/cpuset/myapp
echo "4-7" > /sys/fs/cgroup/cpuset/myapp/cpuset.cpus
echo "0"   > /sys/fs/cgroup/cpuset/myapp/cpuset.mems   # 内存节点 0
# 将进程 PID 写入 tasks 即可
echo <PID> > /sys/fs/cgroup/cpuset/myapp/cgroup.procs

实用建议

  • 对于延迟敏感的服务(如 Redis、Nginx worker),可以将它们绑定到固定的几个核心,并在内核启动参数中使用 isolcpus 将这些核心从调度器的常规负载中隔离出来,进一步减少中断和其他进程的干扰。
  • 避免过度绑定:如果核心数量略大于线程数,可以留出空闲核心处理中断和内核线程。

2. 调度策略调整:为不同负载选择合适的调度类

Linux 支持多种调度策略,主要分为普通调度类(SCHED_OTHER)和实时调度类(SCHED_FIFOSCHED_RR),以及较新的 SCHED_BATCHSCHED_IDLE

  • SCHED_OTHER(CFS 完全公平调度器):默认策略,以时间片轮转按优先级分配 CPU 时间,通过 nice 值(-20 到 +19)调整权重。适合所有常规应用程序。
  • SCHED_FIFO:静态优先级实时调度,高优先级的 FIFO 任务会一直运行直到阻塞或主动让出 CPU。严禁在普通计算任务上使用。
  • SCHED_RR:与 FIFO 类似,但相同优先级的任务会轮流执行时间片。
  • SCHED_BATCH:适合非交互的批处理作业,调度粒度更粗,减少切换。
  • SCHED_IDLE:极低优先级,只在系统空闲时运行。

查看进程的调度策略

chrt -p <PID>          # 显示策略和实时优先级

修改调度策略和优先级

# 将 PID 设置为 SCHED_FIFO 策略,优先级为 80(范围 1~99,数值越大越优先)
chrt -f -p 80 <PID>
# 或者以 SCHED_RR 启动新进程
chrt -r 80 ./my_realtime_task

调整普通进程的 nice 值

renice -n -5 -p <PID>    # 提高优先级(负值表示更高优先级)
nice -n 10 ./batch_job   # 降低优先级

重要警告

  • 实时调度策略一旦滥用(例如一个无限制循环的 FIFO 任务),会完全霸占 CPU,导致整个系统卡死,连 SSH 远程登录都可能无响应。在生产环境修改前务必充分测试,并配合进程绑定隔离核心。
  • 大部分场景中,精心调整 nice 和 cgroup 的 CPU 权重比使用实时调度更安全、更易管理。

3. 中断均衡:将硬件中断分散到多核以减轻单一 CPU 瓶颈

高速网卡、磁盘控制器等硬件会频繁产生中断请求(IRQ)。默认情况下,所有中断可能都涌向 CPU 0,导致核心 0 负载过高(“CPU 0 瓶颈”),而其他核心空闲。通过手动绑定中断处理核心,并借助多队列网卡等硬件特性,可以实现中断负载的均衡。

查看当前中断分布

cat /proc/interrupts          # 列出每个 IRQ 在各 CPU 上的处理次数

手动设置中断亲和性
每个 IRQ 号在 /proc/irq/<IRQ号>/smp_affinity(或 smp_affinity_list)中都有一个位掩码,决定允许哪些核心处理该中断。

# 设定 IRQ 125 只能由 CPU 4~7 处理(十六进制掩码 0xf0 对应 CPU 4,5,6,7)
echo 0f > /proc/irq/125/smp_affinity
# 或者用列表形式(更方便)
echo 4-7 > /proc/irq/125/smp_affinity_list

自动化工具 irqbalance
大部分通用环境推荐使用 irqbalance 守护进程,它会根据系统负载和中断频次自动调整亲和性。但在性能调优场景下,特别是使用 isolcpus 隔离核心后,通常需要关闭 irqbalance,然后手动将中断精确绑定到未隔离的核心上,以避免它对实时核心的扰动。

# 停止并禁用 irqbalance
systemctl stop irqbalance
systemctl disable irqbalance

多队列网卡的 RSS 技术
现代网卡支持接收端缩放(RSS),它会将网络流分成多条硬件队列,每条队列对应独立的中断号。这样不仅可以手动将不同中断绑定到不同核心,还能利用 ethtool 调整哈希字段(如 IP、端口),使流量均匀分布。

# 查看网卡队列数
ethtool -l eth0
# 设置网卡合并中断的参数(NAPI、合并帧数等)
ethtool -C eth0 rx-usecs 20

实用场景

  • 在 NAT 网关或负载均衡器上,将网卡的各队列中断分别绑定到 CPU 的多个核心,同时将转发应用(如 Nginx)绑定到另一些核心,可以显著降低 tail latency。
  • 结合 numactl 确保 IRQ 绑定与处理该流的进程在同一 NUMA 节点上,可进一步减少跨节点内存访问。

总结

  • 进程绑定:用 taskset 或 cgroup cpuset 固定关键进程的位置,结合 isolcpus 实现物理隔离。
  • 调度策略:日常调优从 nice 和 cgroup 权重入手;实时任务严格控制范围并使用专用核心。
  • 中断均衡:先观察 /proc/interrupts,对于高吞吐场景关闭 irqbalance 并手动绑定 IRQ,利用多队列硬件分散处理。

三者结合,正是在高负载下实现稳定、低延迟响应的核心方法。每一次调优前,记得用 perfmpstatsar 等工具建立性能基线,改动后一一验证,避免盲目优化。