记录一个比top更加精准的cpu占用统计脚本

发布时间:2026/10/12 2:08:21
记录一个比top更加精准的cpu占用统计脚本 #!/bin/sh # proc_monitor_sched.sh - 纳秒级精确CPU监控 # 用法: ./proc_monitor_sched.sh 进程名或PID [采样间隔秒] [采样次数] # # 数据源优先级: # 1. /proc/PID/task/*/schedstat (纯整数纳秒所有线程求和) # 2. /proc/PID/task/*/sched (浮点秒所有线程求和) # 3. /proc/PID/stat (jiffies100Hz已含所有线程) TARGET$1 INTERVAL${2:-2} COUNT${3:-0} if [ -z $TARGET ]; then echo Usage: $0 进程名或PID [间隔秒] [次数(0无限)] echo Example: echo $0 Copilot.bin echo $0 1234 1 60 exit 1 fi # 查找PID if echo $TARGET | grep -qE ^[0-9]$; then PID$TARGET else PID$(pidof $TARGET 2/dev/null) if [ -z $PID ]; then PID$(pgrep -x $TARGET 2/dev/null | head -1) fi if [ -z $PID ]; then echo ERROR: 进程 $TARGET 未找到 exit 1 fi fi if [ ! -d /proc/$PID ]; then echo ERROR: PID $PID 不存在 exit 1 fi PROC_NAME$(cat /proc/$PID/comm 2/dev/null || echo unknown) CPU_CORES$(nproc 2/dev/null || grep -c ^processor /proc/cpuinfo 2/dev/null || echo 1) # 自动选择数据源 # 注意: schedstat 和 sched 都是 per-thread 的必须遍历 task/ 下所有线程求和 # /proc/PID/stat 的 utimestime 已经包含所有线程无需遍历 DATASRCnone if [ -d /proc/$PID/task ] [ -n $(cat /proc/$PID/task/*/schedstat 2/dev/null | head -1) ]; then DATASRCschedstat elif [ -d /proc/$PID/task ] [ -n $(grep ^se.sum_exec_runtime /proc/$PID/task/*/sched 2/dev/null | head -1) ]; then DATASRCsched elif [ -r /proc/$PID/stat ]; then DATASRCstat else echo ERROR: 无可用数据源 exit 1 fi case $DATASRC in schedstat) DATASRC_DESCschedstat (纳秒, 所有线程求和) ;; sched) DATASRC_DESCsched (浮点秒, 所有线程求和) ;; stat) DATASRC_DESCstat (jiffies 100Hz, 已含所有线程) ;; esac echo echo 监控进程: $PROC_NAME (PID: $PID) echo CPU核数: $CPU_CORES echo 数据源: $DATASRC_DESC echo 采样间隔: ${INTERVAL}s echo echo printf %-20s %8s %10s %8s %8s %8s %8s\n 时间 CPU% CPU(total) RSS(MB) VM(MB) 线程数 状态 printf %-20s %8s %10s %8s %8s %8s %8s\n ---- ----- ---------- ------- ------ ------ ---- # 读取函数 # schedstat: 遍历所有线程第1字段求和(纳秒) read_schedstat() { awk {s$1} END {printf %.0f, s} /proc/$1/task/*/schedstat 2/dev/null } # sched: 遍历所有线程se.sum_exec_runtime求和(秒浮点) read_sched() { awk /^se.sum_exec_runtime/{s$3} END {printf %.6f, s} /proc/$1/task/*/sched 2/dev/null } # stat: utime(14) stime(15), 已含所有线程, 单位USER_HZ read_stat() { awk {print $14$15} /proc/$1/stat 2/dev/null } # 墙上时钟 read_wall_ns() { awk {printf %.0f, $1 * 1000000000} /proc/uptime } read_wall_hz() { local clk_tck$(getconf CLK_TCK 2/dev/null || echo 100) awk -v tck$clk_tck {printf %.0f, $1 * tck} /proc/uptime } i0 # 初始采样 case $DATASRC in schedstat) t1$(read_schedstat $PID) w1$(read_wall_ns) ;; sched) t1$(read_sched $PID) w1$(awk {print $1} /proc/uptime) ;; stat) CLK_TCK$(getconf CLK_TCK 2/dev/null || echo 100) t1$(read_stat $PID) w1$(read_wall_hz) ;; esac while true; do if [ ! -d /proc/$PID ]; then echo echo [INFO] 进程 PID $PID 已退出 break fi sleep $INTERVAL if [ ! -d /proc/$PID ]; then echo echo [INFO] 进程 PID $PID 已退出 break fi # 第二次采样 case $DATASRC in schedstat) t2$(read_schedstat $PID) w2$(read_wall_ns) # schedstat字段1已是单核等效时间(与stat的utimestime一致) result$(awk -v s1$t1 -v s2$t2 \ -v w1$w1 -v w2$w2 \ -v cores$CPU_CORES BEGIN { ds s2 - s1 dw w2 - w1 if (dw 0) { cpu_single ds / dw * 100 cpu_total ds / dw / cores * 100 printf %.1f %.1f, cpu_single, cpu_total } else { printf 0.0 0.0 } }) ;; sched) t2$(read_sched $PID) w2$(awk {print $1} /proc/uptime) # sched的sum_exec_runtime也是单核等效时间 result$(awk -v s1$t1 -v s2$t2 \ -v w1$w1 -v w2$w2 \ -v cores$CPU_CORES BEGIN { ds s2 - s1 dw w2 - w1 if (dw 0) { cpu_single ds / dw * 100 cpu_total ds / dw / cores * 100 printf %.1f %.1f, cpu_single, cpu_total } else { printf 0.0 0.0 } }) ;; stat) t2$(read_stat $PID) w2$(read_wall_hz) # stat的utimestime也是单核等效时间 result$(awk -v s1$t1 -v s2$t2 \ -v w1$w1 -v w2$w2 \ -v cores$CPU_CORES BEGIN { ds s2 - s1 dw w2 - w1 if (dw 0) { cpu_single ds / dw * 100 cpu_total ds / dw / cores * 100 printf %.1f %.1f, cpu_single, cpu_total } else { printf 0.0 0.0 } }) ;; esac cpu_single$(echo $result | awk {print $1}) cpu_total$(echo $result | awk {print $2}) # 内存 rss$(awk /VmRSS/{print $2} /proc/$PID/status 2/dev/null) vmsize$(awk /VmSize/{print $2} /proc/$PID/status 2/dev/null) rss_mb$((rss / 1024)) vm_mb$((vmsize / 1024)) # 线程数 threads$(ls /proc/$PID/task/ 2/dev/null | wc -l) # 状态 state$(awk /State:/{print $2} /proc/$PID/status 2/dev/null) # 时间 cur_time$(date %H:%M:%S) printf %-20s %7s%% %8s%% %6dMB %6dMB %6d %8s\n \ $cur_time $cpu_single $cpu_total $rss_mb $vm_mb $threads $state # 滚动更新基准 t1$t2 w1$w2 i$((i1)) if [ $COUNT -gt 0 ] [ $i -ge $COUNT ]; then break fi done

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询