CPU使用率计算公式详解:
cpu_usage = (delta_utime + delta_stime) / HZ / delta_time * 100
公式分解:
| 变量 | 含义 | 来源 |
|---|---|---|
delta_utime |
用户态CPU时间差值 | /proc/[pid]/stat 第14列 |
delta_stime |
内核态CPU时间差值 | /proc/[pid]/stat 第15列 |
HZ |
系统时钟频率(通常为100) | getconf CLK_TCK |
delta_time |
两次采集的时间间隔(秒) | 系统uptime差值 |
计算步骤:
步骤1: delta_utime + delta_stime
→ 进程在两次采集之间消耗的总时钟滴答数
步骤2: (delta_utime + delta_stime) / HZ
→ 将时钟滴答数转换为秒
→ 例如:500滴答 / 100 = 5秒
步骤3: (delta_utime + delta_stime) / HZ / delta_time
→ 计算CPU使用率(小数形式)
→ 例如:5秒 / 60秒 = 0.0833
步骤4: * 100
→ 转换为百分比
→ 例如:0.0833 * 100 = 8.33%
举例说明:
假设:
- HZ = 100(每秒100个时钟滴答)
- delta_time = 60秒(两次采集间隔1分钟)
- delta_utime = 300(用户态增加300个滴答)
- delta_stime = 200(内核态增加200个滴答)
计算:
(300 + 200) / 100 / 60 * 100
= 500 / 100 / 60 * 100
= 5 / 60 * 100
= 0.0833 * 100
= 8.33%
结论:该进程在1分钟内平均使用了8.33%的CPU
为什么这样算?
/proc/[pid]/stat记录的是进程从启动到现在的累计CPU时间(时钟滴答数)- 两次采集的差值才是这段时间内实际使用的CPU时间
- 除以
HZ将时钟滴答转换为秒 - 除以
delta_time得到使用率 - 乘以100转换为百分比
注意: 这个公式计算的是单核CPU使用率,最大值可以超过100%(多核情况下)。
具体采集脚本:注:一分钟采集一次
#!/bin/bash
# 功能:采集ksoftirqd和migration进程CPU使用率
# 逻辑:
# - 读取/proc文件系统获取进程CPU时间
# - 保存上次采集数据到文件
# - 计算两次采集之间的CPU使用率差值
# - 按指定格式输出
# 作者:OpenAI
# 输出时间:2026-4-2
# 检查人员:ddd
# 获取脚本所在目录
FILE_DIR="$(cd $(dirname $(readlink -f $0))/ && pwd)"
# 数据文件路径,用于保存上次采集的数据
DATA_FILE="$FILE_DIR/cpu_ksoftirqd_migration.dat"
# 获取系统时钟频率(通常为100,即每秒100个时钟滴答)
HZ=$(getconf CLK_TCK)
# 获取系统启动时间的函数
get_uptime() {
cat /proc/uptime | awk '{print $1}'
}
# 声明关联数组,用于存储当前采集的数据
declare -A curr_data
# 获取当前系统启动时间
uptime=$(get_uptime)
# 遍历所有ksoftirqd和migration进程
while read pid comm; do
# 读取进程的stat文件
stat=$(cat /proc/$pid/stat 2>/dev/null)
# 如果stat文件存在
if [ -n "$stat" ]; then
# 提取用户态CPU时间(第14列)
utime=$(echo "$stat" | awk '{print $14}')
# 提取内核态CPU时间(第15列)
stime=$(echo "$stat" | awk '{print $15}')
# 保存到关联数组:进程名 用户态时间 内核态时间
curr_data[$pid]="$comm $utime $stime"
fi
# 从ps命令输出中获取进程PID和名称
done < <(ps -eo pid,comm | grep -E '(ksoftirqd|migration)/' | awk '{print $1, $2}')
# 如果数据文件存在,读取上次采集的数据
if [ -f "$DATA_FILE" ]; then
# 读取第一行的系统启动时间
prev_uptime=$(head -n 1 "$DATA_FILE")
# 声明关联数组,用于存储上次采集的数据
declare -A prev_data
# 遍历数据文件的每一行(从第2行开始)
while read line; do
# 提取PID
pid=$(echo "$line" | awk '{print $1}')
# 保存整行数据到关联数组
prev_data[$pid]="$line"
# 从第2行开始读取
done < <(tail -n +2 "$DATA_FILE")
fi
# 保存当前数据到文件(覆盖写入)
echo "$uptime" > "$DATA_FILE"
# 遍历当前数据,追加写入文件
for pid in "${!curr_data[@]}"; do
echo "$pid ${curr_data[$pid]}" >> "$DATA_FILE"
done
# 如果存在上次数据,计算CPU使用率
if [ -n "$prev_uptime" ]; then
# 计算两次采集的时间间隔(秒)
delta_time=$(awk "BEGIN {printf \"%.2f\", $uptime - $prev_uptime}")
# 遍历当前数据中的每个进程
for pid in "${!curr_data[@]}"; do
# 如果该进程在上次数据中也存在
if [ -n "${prev_data[$pid]}" ]; then
# 解析当前数据:进程名 用户态时间 内核态时间
read curr_comm curr_utime curr_stime <<< "${curr_data[$pid]}"
# 解析上次数据:PID 进程名 用户态时间 内核态时间
read prev_pid prev_comm prev_utime prev_stime <<< "${prev_data[$pid]}"
# 计算用户态时间差值
delta_utime=$((curr_utime - prev_utime))
# 计算内核态时间差值
delta_stime=$((curr_stime - prev_stime))
# 计算CPU使用率百分比
# 公式:(时间差值 / 时钟频率) / 时间间隔 * 100
cpu_usage=$(awk "BEGIN {printf \"%.1f\", ($delta_utime + $delta_stime) / $HZ / $delta_time * 100}")
# 提取进程名(去掉/后面的核号)
name=${curr_comm%/*}
# 提取CPU核号(去掉/前面的进程名)
core=${curr_comm#*/}
# 输出格式:cpu_进程名@CPU使用率@CPU核号
printf "cpu_%s@%s@%s\n" "$name" "$cpu_usage" "$core"
fi
done
fi
exit 0
0