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

记录一个比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