LinuxNotes

第 14 章:CPU 与调度

zjc 于 2026-01-14 发布

这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 CPU 问题不只是“核数不够”。可能是单线程瓶颈、锁竞争、进程数过多、容器限流、NUMA 不亲和、IO 等待或内存回收。本章从 CPU 指标、进程状态、调度器、优先级、亲和性和容器限制讲起。

14.1 常用指标

系统:

uptime
top
vmstat 1
mpstat -P ALL 1

进程:

pidstat 1
pidstat -u -p <pid> 1
top -H -p <pid>
ps -T -p <pid>

关键指标:

指标 含义
us 用户态 CPU
sy 内核态 CPU
id 空闲
wa 等待 IO
st 虚拟化被抢占,常见于虚拟机
ir 硬中断
cs 每秒上下文切换
runq 运行队列

load average 包含可运行任务和不可中断任务。因此高 load 不一定等于 CPU 忙,还要看 us/sy/wa 和 D 状态进程。

14.2 CPU 信息

lscpu
nproc
cat /proc/cpuinfo
cat /proc/loadavg

关注字段:

字段 含义
CPU(s) 逻辑 CPU 数
Thread(s) per core 每核线程数
Core(s) per socket 每颗 CPU 核数
NUMA node(s) NUMA 节点
Model name CPU 型号
MHz 当前频率

容器内 nproc 可能受 namespace 和运行时影响。资源容量应以编排系统声明的 limit 和宿主机指标共同确认。

14.3 进程状态与运行队列

ps -eo pid,ppid,user,stat,%cpu,%mem,etime,cmd --sort=-%cpu | head -20
vmstat 1 5

典型判断:

现象 可能方向
R 多、us 高 CPU 密集
R 多、sy 高 系统调用、锁、调度或内核路径
D 多、wa 高 IO 或存储异常
cs 很高 频繁切换、线程过多
st 高 宿主机资源竞争

查看 D 状态:

ps -eo pid,stat,wchan:32,cmd | awk '$2 ~ /^D/'

14.4 调度器基础

Linux 使用完全公平调度 CFS 思想。每个可运行任务按权重分配 CPU 时间,而不是严格轮流。

Runnable tasks
  -> runqueue per CPU
     -> scheduler picks task
        -> context switch
           -> user / kernel execution

相关工具:

nice -n 10 ./batch-job
renice -n 10 -p <pid>
taskset -pc <pid>
chrt -p <pid>

普通在线服务不要随意使用实时调度策略,误用可能让系统无法响应。

14.5 上下文切换

查看:

vmstat 1
pidstat -w 1
perf stat -p <pid> -- sleep 10

关注:

指标 含义
cs 总上下文切换
involuntary ctxt switches 非自愿切换
voluntary ctxt switches 自愿等待

非自愿切换高说明 CPU 时间片竞争强;自愿切换高可能是锁、IO 或 sleep 频繁。

14.6 CPU 使用率分析

定位进程:

top
pidstat 1 5
ps -eo pid,user,%cpu,cmd --sort=-%cpu | head

定位线程:

top -H -p <pid>
ps -T -p <pid> -o pid,tid,%cpu,stat,comm --sort=-%cpu

Java 线程栈关联:

printf '%x\n' <tid>
jstack <pid> | grep -A 20 'nid=0x<hex-tid>'

生成火焰图可使用 perf、async-profiler 或运行时自带工具。容器内采集要注意权限和符号。

14.7 CPU 限流

传统工具:

cpulimit -l 50 -p <pid>

cgroup v2:

cat /sys/fs/cgroup/<path>/cpu.max
cat /sys/fs/cgroup/<path>/cpu.stat

Kubernetes:

kubectl get pod <pod> -o jsonpath='{.spec.containers[0].resources}'
kubectl top pod

限流表现:

  1. 应用内部延迟升高;
  2. 宿主机 CPU 不满,但容器 throttled 增长;
  3. GC 或事件循环变慢;
  4. 请求超时增加。

排查:

cat /sys/fs/cgroup/cpu.stat
grep -E 'nr_throttled|throttled_usec' /sys/fs/cgroup/cpu.stat

不同内核和容器运行时路径可能不同,以上是 cgroup v2 的常见位置。

14.8 NUMA 与亲和性

查看:

lscpu | grep NUMA
numactl --hardware
numastat

绑定:

taskset -c 0-3 ./server
taskset -pc 0-3 <pid>
numactl --cpunodebind=0 --membind=0 ./server

NUMA 策略能减少跨节点访问,但手工绑核会降低弹性。数据库等特定 workload 应结合压测和厂商建议设置。

14.9 perf 分析

安装:

sudo apt install linux-tools-common linux-tools-$(uname -r)
sudo dnf install perf

常用:

perf stat -p <pid> -- sleep 10
perf top
perf record -F 99 -p <pid> -g -- sleep 30
perf report

容器和云主机可能限制 PMU 访问。无法采样时,可使用应用运行时 profiler 或 eBPF 工具。

14.10 常见案例

案例一:us 高

pidstat -u 1
top -H -p <pid>

方向:

  1. 单线程算法瓶颈;
  2. 业务流量上升;
  3. 序列化反序列化;
  4. GC;
  5. 正则或加密算法。

案例二:sy 高

strace -cp <pid>
perf top
vmstat 1

方向:

  1. 系统调用过多;
  2. 小包网络;
  3. 频繁线程切换;
  4. 锁竞争;
  5. 内存页缺失。

案例三:容器 CPU 未满但延迟高

kubectl top pod
cat /sys/fs/cgroup/cpu.stat

结论可能是 CPU limit 触发限流。处理:

  1. 提高 limit;
  2. 优化算法;
  3. 降低单实例流量;
  4. 扩容;
  5. 调整线程数。

本章小结

CPU 分析先看整体利用率、运行队列、iowait、steal 和上下文切换,再定位进程和线程。高 load 不一定是 CPU 不足,D 状态和 IO 等待同样会推高 load。容器环境必须同时看宿主机和 cgroup 限流指标。

思考题

  1. load 高但 idle 也高,可能是什么原因?
  2. us、sy、wa、st 分别说明什么?
  3. 如何把 Linux 线程 ID 关联到 Java 线程栈?
  4. CPU throttling 为什么会造成延迟上升?
  5. 设计一套服务 CPU 水位和容量告警。