Linux 下計算 CPU 負載

系統整體的 CPU 負載資訊可從以下檔案中取得

/proc/stat

內容如下

$ cat /proc/stat 
cpu  66834 263 18827 477280 28092 0 103 0 0 0
cpu0 17006 67 4812 115682 10204 0 56 0 0 0
cpu1 16422 68 4320 120148 6861 0 8 0 0 0
cpu2 16875 62 5155 119107 6590 0 28 0 0 0
cpu3 16530 64 4539 122341 4436 0 10 0 0 0
intr 1273232 21 3140 0 0 0 0 0 0 1 86 0 0 140 0 0 0 0 0 0 0 0 0 0 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 12298 24192 68472 14 602 59376 7876 44 1007 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 7302295
btime 1452592510
processes 7985
procs_running 1
procs_blocked 0
softirq 1088945 6 379642 247 41240 68596 0 4054 291005 0 304155

man proc 中的內容說明不全, 可參考 kernel source tree 中的文檔

Documentation/filesystems/proc.txt

在線版點這裡.

我們需要的是前幾行 cpu 開頭的資訊, 各欄說明如下

- user: normal processes executing in user mode
- nice: niced processes executing in user mode
- system: processes executing in kernel mode
- idle: twiddling thumbs
- iowait: waiting for I/O to complete
- irq: servicing interrupts
- softirq: servicing softirqs
- steal: involuntary wait
- guest: running a normal guest
- guest_nice: running a niced guest

單位為 USER_HZ. 但如果我們要計算百分比, 那就不需要花時間轉換成秒了.

/proc/stat 中的值皆為 counter, 從開機後不斷的遞增, 所以要計算前一秒在 user space 的時間花用, 就將目前 user 欄的值 (user1) 減去一秒前 user 欄的值 (user0) 即可. 各欄加總起來為總執行時間的花用 (total), 所以, 要計算上一秒 user space 花用時間的百分比, 可以下列算式完成

100 * (user1 - user0) / (total1 - total0)

Python 範例如下

import io
import time
import os

class CpuStat:
    all = None
    cpu = None

    def __init__(self, cpu = None):
        self.old_user = 0
        self.old_nice = 0
        self.old_sys = 0
        self.old_total = 0
        self.user = 0
        self.sys = 0
        self.nice = 0
        self.total = 0
        self.cpu = cpu

    def update(self, stat_line):
        cols = stat_line.split()

        tmp = int(cols[1])
        self.user = tmp - self.old_user
        self.old_user = tmp

        tmp = int(cols[2])
        self.nice = tmp - self.old_nice
        self.old_nice = tmp

        tmp = int(cols[3])
        self.sys = tmp - self.old_sys
        self.old_sys = tmp

        tmp = self.old_user + self.old_nice + self.old_sys + int(cols[4]) + \
              int(cols[5]) + int(cols[6]) + int(cols[7]) + int(cols[8]) + \
              int(cols[9]) + int(cols[10])
        self.total = tmp - self.old_total
        self.old_total = tmp

    def __str__(self):
        return "user={0}, nice={1}, sys={2}, total={3}".format(self.user,
                                                               self.sys,
                                                               self.nice,
                                                               self.total)

cpu_stats = {}

while True:
    f = io.open("/proc/stat", "r", encoding="utf-8")

    os.system("clear")

    while True:
        line = f.readline()
        if len(line) < 3 or "cpu" != line[0:3]:
            break

        if "cpu" == line[0:3]:
            cpu = line[3:line.find(" ")]
            if len(cpu) > 0:
                cpu = int(cpu)
            else:
                cpu = -1

        if cpu not in cpu_stats:
            stat = CpuStat(cpu)
            cpu_stats[cpu] = stat
        else:
            stat = cpu_stats[cpu]

        stat.update(line)

        print "{0}".format(100 * stat.user / stat.total + \
                           100 * stat.nice / stat.total + \
                           100 * stat.sys / stat.total)

    f.close()

    time.sleep(1)