linux资源监控——计算CPU利用率

Posted by Rpl on June 20, 2019

前言

linux资源监控系列文章:

  1. linux资源监控——计算CPU利用率
  2. linux资源监控——获取GPU信息
  3. linux资源监控——获取Memory与Swap的使用率

一 通过top查看cpu各类率占用信息

如下图所示:

       
us User time 用户时间 表示CPU 执行用户进程的时间,包括nice时间。通常期望用户空间CPU 越高越好
sy System time 系统时间 表示CPU 在内核运行时间,包括IRQ 和softirq 时间。系统CPU 占用率高,表明系统某部分存在瓶颈,通常值越低越好
ni Nice time 优化时间 系统调整进程优先级所花费的时间。
id Idle time 空闲时间 系统处于空闲期,等待进程运行的时间。
wa Waiting time 等待时间 CPU 在等待I/O 操作完成所花费的时间。系统不应该花费大量时间来等待I/O 操, 否则说明I/O存在瓶颈。
hi Hard Irq time 硬中断处理时间 系统处理硬中断所花费的时间。
si Soft Irq time 软中断处理时间 系统处理软中断所花费的时间。
st Steal time 丢失时间 被强制等待(involuntary wait )虚拟 CPU 的时间,此时 hypervisor 在为另一个虚拟处理器服务。

二 通过/proc/stat文件查看cpu信息

在linux系统中,/proc/stat文件记录了cpu活动的各类详细信息,我们可以通过这个文件来计算出cpu的利用率。值得一提的是文件中的所有值都是系统自开机时刻到当前的累计值。

首先,我们来看看/proc/stat文件里具体有什么信息:

1
cat /proc/stat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cpu  143981718 1653 19835190 651219576 474793 0 1795548 0 0 0
cpu0 4922748 116 634217 35211530 33025 0 1054 0 0 0
cpu1 7264010 6 724979 32772529 27436 0 87110 0 0 0
cpu2 7265280 106 1592936 31897031 33649 0 85197 0 0 0
cpu3 8825503 29 785288 31149702 27791 0 75427 0 0 0
cpu4 7864655 10 813419 32075626 28549 0 107073 0 0 0
cpu5 8649335 190 1348528 30743452 15312 0 84047 0 0 0
cpu6 6918422 12 745619 33098715 35480 0 96065 0 0 0
cpu7 6911914 2 1503204 32343557 31685 0 107606 0 0 0
cpu8 8067154 301 760417 31947748 23598 0 96948 0 0 0
cpu9 7956319 268 763990 32045354 20748 0 112129 0 0 0
cpu10 8350317 10 863553 31466332 42994 0 113779 0 0 0
cpu11 6958231 279 751582 32980023 23059 0 82738 0 0 0
cpu12 6857693 30 1383365 32437960 30222 0 102721 0 0 0
cpu13 6027713 73 896910 33822287 19033 0 89450 0 0 0
cpu14 6611466 1 787552 33365812 14534 0 100703 0 0 0
cpu15 6244280 2 2040406 32445853 20147 0 98755 0 0 0
cpu16 8117104 8 1173451 31472941 15606 0 86714 0 0 0
cpu17 7633190 104 785277 32361532 11750 0 88718 0 0 0
cpu18 6345486 2 778699 33674417 12593 0 88518 0 0 0
cpu19 6190891 95 701790 33907166 7573 0 90789 0 0 0
intr 7124054096 26 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 39 0 0 47 0 0 0 89508674 0 0 0 0 0 0 0 0 0 0 0 0 0 101 0 0 0 0 0 0 0 0 365553 45796 0 326675 306514 322983 156258 166277 168551 115525 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 203663 0 0 58842159 179417102 160744835 182006220 189160682 198394762 178230438 182873083 178224428 186110541 247087544 210543678 216684749 183073272 204699369 200669565 216376267 208119645 199156143 194237301 1283 24079457 9 1062 2227 2570 9511496 6479573 9056319 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 7627984946
btime 1560589914
processes 18553467
procs_running 1
procs_blocked 0
softirq 5913064204 17 744470697 11078247 3780395442 47291 0 11998890 583315978 0 781757642

行参数信息:

   
cpu 是所有cpu信息的总和值,我们之后计算cpu利用率用的就是这条信息
cpu0, cpu1, … , cpu19 是各个cpu的信息。(本服务器是20核cpu)
intr 是中断信息,第一个值是自系统启动以来,所发生的中断次数。
ctxt 是系统自启动以来cpu发生的上下文交换次数。
btime 是系统启动以来到当前位置的时间,单位秒。(资料上都是这么说的,但个人感觉很像一个时间戳)
processes 系统自启动以来创建的进程数。
procs_running 当前运行队列的任务的数目。
procs_blocked 当前被阻塞的任务数目

列参数信息(以上每行cpu数字代表的含义):

1
cpu 143981718 1653 19835190 651219576 474793 0 1795548 0 0 0
  user nice system idle iowait irq softirq  
cpu 143981718 1653 19835190 651219576 474793 0 1795548 0 0 0
   
user 从系统启动开始累计到当前时刻,用户态的CPU时间(单位:jiffies) ,不包含 nice值为负进程。1jiffies=0.01秒
nice 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间(单位:jiffies)
system 从系统启动开始累计到当前时刻,核心时间(单位:jiffies)
idle 从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其它空闲时间(单位:jiffies)
iowait 从系统启动开始累计到当前时刻,硬盘IO等待时间(单位:jiffies)
irq 从系统启动开始累计到当前时刻,硬中断时间(单位:jiffies)
softirq 从系统启动开始累计到当前时刻,软中断时间(单位:jiffies)

三 cpu占用率计算公式

需要注意的是由于/proc/stat文件中的各项值都是系统自启动以来的累计值。如果我们需要计算某时刻的cpu利用率,需要连续取2次文件的值,然后各自相减,再计算。 也就是:

cpu_total = cpu_total_2 - cpu_total_1

user = user_2 - user_1

nice = nice_2 - nice_1

idle = idle_2 - idle_1
......

top中的各类cpu利用率计算公式:

cpu_total= user + nice + system + idle + iowait + irq + softirq

us = (user + nice ) / cpu_total * 100%

sy = (systime + irq + softirq ) / cpu_total * 100%

id = idle / cpu_total * 100%

ni = nice / cpu_total * 100%

wa = iowait / cpu_total * 100%

hi = irq / cpu_total * 100%

si = softirq / cpu_total * 100%

(st 基本为0)

一般来说,如果我们只需要粗略计算出系统总的CPU的利用率,可以只取/proc/stat文件 cpu 这一行的值就可以了。因为这个cpu是所有子核的cpu总值。

此外,

cpu usage = (1 - idle / cpu_total) * 100% (除去cpu空闲时间外的其他所有cpu使用率,也可粗略计算为cpu利用率)

或者,

cpu usage = (user + nice + system)/ cpu_total * 100%


四 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import os
import time
import signal
import subprocess

# 获取cpu信息
def get_CpuInfo(ip):

    total_list = []
    idle_list = []
    timeout_seconds = 30

    pcpu = '-1'

    try:
        for k in range(2):
            data = []
            cpu_cmd = 'ssh -q -o StrictHostKeyChecking=no %s cat /proc/stat |grep -w cpu' % ip

            # res = os.popen(cpu_cmd, ).read().split()
            res = timeout_Popen(cpu_cmd, timeout=timeout_seconds)
            res = res.stdout.read().split()

            if not res:
                print('ssh %s 连接失败, 获取cpu信息失败' % ip)
                return pcpu

            for i in res:
                try:
                    if isinstance(eval(i), int):
                        data.append(i)
                except:
                    continue

            # print('cpu:' + str(data))
            total_cpu_time = sum([int(i) for i in data])
            total_list.append(total_cpu_time)
            idle_list.append(int(data[3]))
    except:
        pass

    if len(total_list) == 2:
        total = total_list[1] - total_list[0]
        idle = idle_list[1] - idle_list[0]
        pcpu = str(round(100 * (total - idle) / total, 2))
    else:
        print('{}: timeout > {}s, 获取cpu信息失败'.format(ip, timeout_seconds))

    return pcpu

# 处理popen等待超时:
def timeout_Popen(cmd, timeout=30):
    start = time.time()
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    while process.poll() is None:  # 是否结束
        time.sleep(0.2)
        now = time.time()
        if now - start >= timeout:
            os.kill(process.pid, signal.SIGKILL)

            # pid=-1 等待当前进程的all子进程, os.WNOHANG 没有子进程退出,
            os.waitpid(-1, os.WNOHANG)
            return None

    return process