这是《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
限流表现:
- 应用内部延迟升高;
- 宿主机 CPU 不满,但容器 throttled 增长;
- GC 或事件循环变慢;
- 请求超时增加。
排查:
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>
方向:
- 单线程算法瓶颈;
- 业务流量上升;
- 序列化反序列化;
- GC;
- 正则或加密算法。
案例二:sy 高
strace -cp <pid>
perf top
vmstat 1
方向:
- 系统调用过多;
- 小包网络;
- 频繁线程切换;
- 锁竞争;
- 内存页缺失。
案例三:容器 CPU 未满但延迟高
kubectl top pod
cat /sys/fs/cgroup/cpu.stat
结论可能是 CPU limit 触发限流。处理:
- 提高 limit;
- 优化算法;
- 降低单实例流量;
- 扩容;
- 调整线程数。
本章小结
CPU 分析先看整体利用率、运行队列、iowait、steal 和上下文切换,再定位进程和线程。高 load 不一定是 CPU 不足,D 状态和 IO 等待同样会推高 load。容器环境必须同时看宿主机和 cgroup 限流指标。
思考题
- load 高但 idle 也高,可能是什么原因?
- us、sy、wa、st 分别说明什么?
- 如何把 Linux 线程 ID 关联到 Java 线程栈?
- CPU throttling 为什么会造成延迟上升?
- 设计一套服务 CPU 水位和容量告警。