计算每个进程的 CPU

jac*_*vis 5 bash cpu-usage

我正在尝试编写一个脚本来返回特定进程的 CPU 使用率(以 % 为单位)我需要使用 /proc/PID/stat 因为嵌入式系统上不存在 ps aux。

我试过这个:

#!/usr/bin/env bash

PID=$1

PREV_TIME=0
PREV_TOTAL=0
while true;do
    TOTAL=$(grep '^cpu ' /proc/stat |awk '{sum=$2+$3+$4+$5+$6+$7+$8+$9+$10; print sum}')

    sfile=`cat /proc/$PID/stat`

    PROC_U_TIME=$(echo $sfile|awk '{print $14}')
    PROC_S_TIME=$(echo $sfile|awk '{print $15}')
    PROC_CU_TIME=$(echo  $sfile|awk '{print $16}')
    PROC_CS_TIME=$(echo $sfile|awk '{print $17}')

    let "PROC_TIME=$PROC_U_TIME+$PROC_CU_TIME+$PROC_S_TIME+$PROC_CS_TIME"

    CALC="scale=2 ;(($PROC_TIME-$PREV_TIME)/($TOTAL-$PREV_TOTAL)) *100"

    USER=`bc <<< $CALC`

    PREV_TIME="$PROC_TIME"
    PREV_TOTAL="$TOTAL"

    echo $USER
    sleep 1
done
Run Code Online (Sandbox Code Playgroud)

但是,如果我将其与顶部进行比较,则不会给出正确的值。你们中的一些人知道我哪里出错了吗?

谢谢

Chr*_*lan 3

在正常调用top(无参数)下,该 %CPU列是一段时间内进程使用的滴答数与一个 CPU 提供的总滴答数的比例。

根据 top.c 源代码,该%CPU字段计算如下:

float u = (float)p->pcpu * Frame_tscale;
Run Code Online (Sandbox Code Playgroud)

其中进程的 pcpu 是自上次显示以来经过的用户时间 + 系统时间:

hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
...
   if(ptr) tics -= ptr->tics;
...
// we're just saving elapsed tics, to be converted into %cpu if
// this task wins it's displayable screen row lottery... */
this->pcpu = tics;
Run Code Online (Sandbox Code Playgroud)

和:

et = (timev.tv_sec - oldtimev.tv_sec)
     + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));
Run Code Online (Sandbox Code Playgroud)

Hertz在大多数系统上为 100 滴答/秒 ( grep 'define HZ' /usr/include/asm*/param.h),et是自最后显示的帧以来经过的时间(以秒为单位),并且Cpu_tot是 CPU 的数量(但默认使用 1)。

因此,对于 T 秒内的进程使用每秒 100 个时钟周期的系统,方程为:

(curr_utime + curr_stime - (last_utime + last_stime)) / (100 * T) * 100
Run Code Online (Sandbox Code Playgroud)

脚本变为:

#!/bin/bash
PID=$1
SLEEP_TIME=3 # seconds
HZ=100       # ticks/second
prev_ticks=0
while true; do
    sfile=$(cat /proc/$PID/stat)

    utime=$(awk '{print $14}' <<< "$sfile")
    stime=$(awk '{print $15}' <<< "$sfile")
    ticks=$(($utime + $stime))

    pcpu=$(bc <<< "scale=4 ; ($ticks - $prev_ticks) / ($HZ * $SLEEP_TIME) * 100")

    prev_ticks="$ticks"

    echo $pcpu
    sleep $SLEEP_TIME
done
Run Code Online (Sandbox Code Playgroud)

此方法与原始脚本之间的主要区别在于,top 是针对 1 个 CPU 计算其 CPU 时间百分比,而您尝试针对所有 CPU 的总时间来计算。确实,您可以通过执行 来计算一段时间内的精确聚合刻度Hertz * time * n_cpus,并且 /proc/stat 中的数字不一定会正确求和:

$ grep 'define HZ' /usr/include/asm*/param.h
/usr/include/asm-generic/param.h:#define HZ 100
$ grep ^processor /proc/cpuinfo | wc -l
16
$ t1=$(awk '/^cpu /{sum=$2+$3+$4+$5+$6+$7+$8+$9+$10; print sum}' /proc/stat) ; sleep 1 ; t2=$(awk '/^cpu /{sum=$2+$3+$4+$5+$6+$7+$8+$9+$10; print sum}' /proc/stat) ; echo $(($t2 - $t1))
1602
Run Code Online (Sandbox Code Playgroud)