准确计算Linux中以百分比给出的CPU使用率?

新手上路,请多包涵

这是一个已经被问过很多次的问题,但是我找不到得到充分支持的答案。

很多人建议使用 top 命令,但如果你运行一次 top(因为你有一个脚本,例如每 1 秒收集一次 Cpu 使用情况),它总是会给出相同的 Cpu 使用结果( 示例 1示例 2 )。

计算 CPU 使用率的更准确方法是从 /proc/stat 中读取值,但大多数答案仅使用 /proc/stat 中的前 4 个字段来计算( 这里 有一个示例) .

/proc/stat/ 从 Linux 内核 2.6.33 开始,每个 CPU 内核有 10 个字段!

我还发现这个 Accurately Calculating CPU Utilization in Linux using /proc/stat 问题指出了同样的问题——大多数其他问题只考虑了众多领域中的 4 个——但这里给出的答案仍然以“我think”(不确定),除此之外,它只关注前 7 个字段(在 /proc/stat/ 中的 10 个字段中)

这个 perl 脚本使用所有字段来计算 CPU 使用率,经过进一步调查,我再次认为这不正确。

After taking a quick look into the kernel code here , it looks like, for example, guest_nice and guest fields are always increasing together with nice and user (因此它们不应包含在 CPU 使用率计算中,因为它们已包含在 niceuser 字段中)

 /*
 * Account guest cpu time to a process.
 * @p: the process that the cpu time gets accounted to
 * @cputime: the cpu time spent in virtual machine since the last update
 * @cputime_scaled: cputime scaled by cpu frequency
 */
static void account_guest_time(struct task_struct *p, cputime_t cputime,
                   cputime_t cputime_scaled)
{
    u64 *cpustat = kcpustat_this_cpu->cpustat;

    /* Add guest time to process. */
    p->utime += cputime;
    p->utimescaled += cputime_scaled;
    account_group_user_time(p, cputime);
    p->gtime += cputime;

    /* Add guest time to cpustat. */
    if (task_nice(p) > 0) {
        cpustat[CPUTIME_NICE] += (__force u64) cputime;
        cpustat[CPUTIME_GUEST_NICE] += (__force u64) cputime;
    } else {
        cpustat[CPUTIME_USER] += (__force u64) cputime;
        cpustat[CPUTIME_GUEST] += (__force u64) cputime;
    }
}

那么总结一下,Linux中计算CPU使用率的准确方法是什么,计算中应该考虑哪些字段以及如何(哪些字段归因于空闲时间,哪些字段归因于非空闲时间)?

原文由 Vangelis Tasoulas 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

根据 htop 源代码,我的假设看起来是有效的:

(参见 static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) 的函数)

 // Guest time is already accounted in usertime
usertime = usertime - guest;                             # As you see here, it subtracts guest from user time
nicetime = nicetime - guestnice;                         # and guest_nice from nice time
// Fields existing on kernels >= 2.6
// (and RHEL's patched kernel 2.4...)
unsigned long long int idlealltime = idletime + ioWait;  # ioWait is added in the idleTime
unsigned long long int systemalltime = systemtime + irq + softIrq;
unsigned long long int virtalltime = guest + guestnice;
unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;

因此,从 /proc/stat 的第一行中列出的字段中:(请参阅 文档 中的第 1.8 节)

      user    nice   system  idle      iowait irq   softirq  steal  guest  guest_nice
cpu  74608   2520   24433   1117073   6176   4054  0        0      0      0

从算法上讲,我们可以计算 CPU 使用率,如下所示:

 PrevIdle = previdle + previowait
Idle = idle + iowait

PrevNonIdle = prevuser + prevnice + prevsystem + previrq + prevsoftirq + prevsteal
NonIdle = user + nice + system + irq + softirq + steal

PrevTotal = PrevIdle + PrevNonIdle
Total = Idle + NonIdle

# differentiate: actual value minus the previous one
totald = Total - PrevTotal
idled = Idle - PrevIdle

CPU_Percentage = (totald - idled)/totald

原文由 Vangelis Tasoulas 发布,翻译遵循 CC BY-SA 4.0 许可协议

以下是基于 Vangelis 回答的 bash 脚本。它产生如下输出:

 total 49.1803
cpu0 14.2857
cpu1 100
cpu2 28.5714
cpu3 100
cpu4 30
cpu5 25

创建一个名为 get_cpu_usage.sh

使用以下命令运行它: bash get_cpu_usage.sh 0.2

参数是要测量的秒数。在这种情况下,它是 200 毫秒。

内容是:

 #!/bin/sh

sleepDurationSeconds=$1

previousDate=$(date +%s%N | cut -b1-13)
previousStats=$(cat /proc/stat)

sleep $sleepDurationSeconds

currentDate=$(date +%s%N | cut -b1-13)
currentStats=$(cat /proc/stat)

cpus=$(echo "$currentStats" | grep -P 'cpu' | awk -F " " '{print $1}')

for cpu in $cpus
do
    currentLine=$(echo "$currentStats" | grep "$cpu ")
    user=$(echo "$currentLine" | awk -F " " '{print $2}')
    nice=$(echo "$currentLine" | awk -F " " '{print $3}')
    system=$(echo "$currentLine" | awk -F " " '{print $4}')
    idle=$(echo "$currentLine" | awk -F " " '{print $5}')
    iowait=$(echo "$currentLine" | awk -F " " '{print $6}')
    irq=$(echo "$currentLine" | awk -F " " '{print $7}')
    softirq=$(echo "$currentLine" | awk -F " " '{print $8}')
    steal=$(echo "$currentLine" | awk -F " " '{print $9}')
    guest=$(echo "$currentLine" | awk -F " " '{print $10}')
    guest_nice=$(echo "$currentLine" | awk -F " " '{print $11}')

    previousLine=$(echo "$previousStats" | grep "$cpu ")
    prevuser=$(echo "$previousLine" | awk -F " " '{print $2}')
    prevnice=$(echo "$previousLine" | awk -F " " '{print $3}')
    prevsystem=$(echo "$previousLine" | awk -F " " '{print $4}')
    previdle=$(echo "$previousLine" | awk -F " " '{print $5}')
    previowait=$(echo "$previousLine" | awk -F " " '{print $6}')
    previrq=$(echo "$previousLine" | awk -F " " '{print $7}')
    prevsoftirq=$(echo "$previousLine" | awk -F " " '{print $8}')
    prevsteal=$(echo "$previousLine" | awk -F " " '{print $9}')
    prevguest=$(echo "$previousLine" | awk -F " " '{print $10}')
    prevguest_nice=$(echo "$previousLine" | awk -F " " '{print $11}')

    PrevIdle=$((previdle + previowait))
    Idle=$((idle + iowait))

    PrevNonIdle=$((prevuser + prevnice + prevsystem + previrq + prevsoftirq + prevsteal))
    NonIdle=$((user + nice + system + irq + softirq + steal))

    PrevTotal=$((PrevIdle + PrevNonIdle))
    Total=$((Idle + NonIdle))

    totald=$((Total - PrevTotal))
    idled=$((Idle - PrevIdle))

    CPU_Percentage=$(awk "BEGIN {print ($totald - $idled)/$totald*100}")

    if [[ "$cpu" == "cpu" ]]; then
        echo "total "$CPU_Percentage
    else
        echo $cpu" "$CPU_Percentage
    fi
done

原文由 Fidel 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题