排查systemd服务调度异常需逐层验证:先查cpuaffinity/cpuset配置,再读/proc/pid/status中cpus_allowed,用taskset -cp确认实际运行核,结合mpstat、perf record sched:sched_migrate_task分析迁移行为,并检查cgroup throttling、systemd卡顿及内核日志警告。

排查 systemd 服务在多核 CPU 下的调度异常,核心是确认服务进程是否被正确绑定到预期 CPU 核心,以及其运行状态是否受 systemd、cgroup 或内核调度器干扰。这不是单纯看“CPU 使用率高不高”,而是要验证“该跑在哪、实际跑在哪、为什么没按预期跑”。
查服务实际绑核配置与运行位置
systemd 服务可通过 CPUAffinity 或 CPUSet 控制绑核,但最终生效依赖 cgroup v2(或 v1)和内核调度器。需逐层比对:
- 查看服务单元文件中是否显式设置:
systemctl cat your-service.service | grep -E "(CPUAffinity|CPUSet)"
若设了CPUAffinity=0-1,说明期望绑定到 CPU 0 和 1;若设了CPUSet=0,1(需 cgroup v2),则更严格限制可运行核 - 检查运行时实际生效的 cpuset:
cat /proc/$(systemctl show -p MainPID your-service.service | cut -d= -f2)/status | grep Cpus_allowed
输出如Cpus_allowed: 00000001(十六进制位图)表示只允许在 CPU 0 运行;若为00000003,则允许 CPU 0 和 1 - 确认进程当前正在哪个核上运行:
taskset -cp $(systemctl show -p MainPID your-service.service | cut -d= -f2)
该命令直接显示内核调度器当前分配的 CPU,可能与Cpus_allowed不同(例如刚启动、负载低时未真正迁移)
看调度行为是否异常(如频繁迁移、空转或卡 idle)
绑核后仍出现延迟抖动或利用率不均,很可能是调度器未按预期维持亲和性,或被其他机制干扰:
- 用
mpstat -P ALL 1持续观察各核的 %usr/%sy/%idle,对比服务应绑定的核心与其他核的差异。若目标核长期%idle很高,但服务本应持续计算,说明它没真正在那跑 - 检查是否被 cgroup freezer 或 systemd 的 TasksMax/MemoryMax 限制造成阻塞:
systemctl show your-service.service | grep -E "(TasksMax|MemoryMax|CPUWeight)"
资源硬限制可能触发内核 throttling,表现为进程周期性进入D或R+状态,cat /sys/fs/cgroup/system.slice/your-service.service/cpu.stat中nr_throttled非零即为证据 - 抓取调度事件确认是否被强制迁移:
sudo perf record -e sched:sched_migrate_task -p $(systemctl show -p MainPID your-service.service | cut -d= -f2) -- sleep 10
再用perf script查看是否有大量migrate_task记录——这说明内核在反复把该进程从一个核移到另一个核,违背绑核意图
排除 systemd 自身或内核层干扰
某些 systemd 版本(尤其搭配较新 runc/containerd)在重载或重启服务时,会错误重置 cgroup cpuset,导致多个服务意外共享同一组 CPU。同时,systemd 卡顿本身也会让子服务调度失序:
- 执行
systemctl list-jobs,确认无长时间running的 job(如start your-service.service卡住),否则服务可能处于“半启动”状态,pid 已分配但 cgroup 未就绪 - 检查 systemd 是否响应正常:
busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager UnitPath
若超时或报错,说明 D-Bus 通信中断,systemctl 命令不可信,需优先修复 systemd 本身 - 查看内核日志中是否有关于 cgroup 或调度器的警告:
dmesg -T | grep -i "cgroup\|sched\|throttle"
特别注意cgroup: fork rejected by pids controller或sched: RT throttling activated类提示
验证绑核是否真正生效(实测法)
理论配置 ≠ 实际效果。最可靠的方式是注入可控负载并观测:
- 临时给服务加一个简单 CPU 密集型子任务(如
stress-ng --cpu 1 --timeout 30s),确保它确实能打满单核 - 用
psr字段确认线程绑定:ps -eLo pid,tid,psr,comm --sort=-pcpu | grep your-servicepsr列显示当前运行核编号,连续采样多次,看是否稳定在预期值 - 若发现不稳定,尝试在 service 文件中增加:
CPUSchedulingPolicy=otherCPUSchedulingPriority=0
避免被实时调度策略(如 SCHED_FIFO)意外抢占或迁移











