perf top 默认不区分用户态与内核态,需用 -K -g --call-graph dwarf -p 参数配合函数前缀识别态分布,并通过 perf script | awk 或 /proc/pid/stat 验证 user/kernel 时间占比。

直接用 perf top 观察 Nginx worker 的内核态与用户态时间占比,效果有限——它默认展示的是函数级采样热点,不直接区分态(user/kernel),也不按时间占比聚合。要准确拆解 user/kernel 时间分布,需配合正确参数和后续分析逻辑。
核心操作:启用内核栈采样并过滤 worker 进程
运行以下命令,聚焦于正在运行的 Nginx worker 进程(假设其 PID 已知,或用 pgrep nginx 获取):
perf top -p $(pgrep -f "nginx: worker") -g --call-graph dwarf -K
其中关键参数说明:
-
-K:强制显示内核态符号(需内核调试信息包已安装,如linux-image-$(uname -r)-dbgsym) -
-g --call-graph dwarf:启用带调用栈的 DWARF 解析,能穿透系统调用边界,区分用户函数进入内核后的路径(如epoll_wait→do_epoll_wait→__sys_epoll_wait) -
-p:精确绑定到 worker 进程,避免混入 master 或其他进程噪声
识别 user/kernel 分布的关键线索
perf top 界面中,函数名前缀是判断态的直接依据:
- 以
nginx、ngx_、libc-、libpthread-开头的符号,基本属于用户态执行路径 - 以
do_、__sys_、ep_poll、tcp_、ext4_、__softirqentry_text_start等开头的,属于内核态函数 - 特别注意
system_call_fastpath或entry_SYSCALL_64—— 它们是用户态陷入内核的“门”,其下方调用栈即为内核态耗时主体
观察顶部 10–20 行,统计 user 前缀与 kernel 前缀函数的采样占比之和,即可粗略估算 user/kernel 时间分布。例如:若 ngx_http_process_request、ngx_event_accept 等占 65%,而 do_epoll_wait、tcp_recvmsg、ext4_writepages 占 28%,剩余为调度/中断开销,则 user/kernel 比约为 65% : 28%。
更精准的做法:导出报告后按态过滤
实时 perf top 不便于统计,推荐转为离线分析:
- 采集 30 秒:
perf record -p $(pgrep -f "nginx: worker") -g --call-graph dwarf -K sleep 30 - 生成按态分类的火焰图或汇总报告:
perf script | awk '$3 ~ /^(do_|__sys_|ep_|tcp_|ext4_|__softirq)/ {k++} $3 !~ /^(do_|__sys_|ep_|tcp_|ext4_|__softirq)/ {u++} END {print "user:", u, "kernel:", k, "ratio:", u/k+0}'
该脚本通过第三列(symbol name)正则匹配典型内核函数前缀,自动计数并输出比值。注意:若结果中 k 为 0,说明未捕获到有效内核栈(可能缺 debuginfo 或内核未开启 CONFIG_FRAME_POINTER)。
结合 /proc/[pid]/stat 验证结果
作为交叉校验,可读取 worker 进程的 stat 文件:
cat /proc/$(pgrep -f "nginx: worker")/stat | awk '{print "utime:", $14, "stime:", $15}'
$14 是用户态 jiffies 累计值,$15 是内核态 jiffies 累计值。两者比值与 perf 采样趋势应基本一致。若 perf 显示 kernel 占比高但 stat 中 stime 很小,大概率是采样未覆盖到真实瓶颈(如短时硬中断、高频软中断未被 dwarf 栈捕获),此时应改用 perf record -e irq:softirq_entry,irq:softirq_exit 单独抓软中断。


















