【AI生成】系统文件:`/proc/stat``/proc/$pid/stat`之获取单进程cpu使用率

蛋蛋 2026年04月02日 2 0

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

为什么这样算?

  1. /proc/[pid]/stat 记录的是进程从启动到现在的累计CPU时间(时钟滴答数)
  2. 两次采集的差值才是这段时间内实际使用的CPU时间
  3. 除以 HZ 将时钟滴答转换为秒
  4. 除以 delta_time 得到使用率
  5. 乘以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
Last Updated: 2026/04/02 17:13:15
【AI生成】学霸笔记:25|面向 CDN 运维工程师的 AI 原生工作流落地指南 【AI生成】系统文件:`/proc/stat``/proc/$pid/stat`