准确计算Linux中以百分比给出的CPU使用率?
Posted
技术标签:
【中文标题】准确计算Linux中以百分比给出的CPU使用率?【英文标题】:Accurate calculation of CPU usage given in percentage in Linux? 【发布时间】:2014-06-15 13:54:39 【问题描述】:这是一个被问过很多次的问题,但是我找不到得到充分支持的答案。
很多人建议使用 top 命令,但是如果你运行一次 top(因为你有一个脚本,例如每 1 秒收集一次 Cpu 使用情况)它总是会给出相同的 Cpu 使用结果(example 1,example 2 )。
一种更准确的计算 CPU 使用率的方法是读取来自 /proc/stat
的值,但大多数答案仅使用来自 /proc/stat
的前 4 个字段来计算它(一个示例 here)。
/proc/stat/
自 Linux 内核 2.6.33 起每个 CPU 内核有 10 个字段!
我还发现了这个Accurately Calculating CPU Utilization in Linux using /proc/stat 问题,它指出了同样的问题——大多数其他问题只考虑了众多领域中的 4 个——但这里给出的答案仍然以“我认为”开头(不确定),除此之外,它只关注前 7 个字段(/proc/stat/
中的 10 个字段)
This perl 脚本使用所有字段来计算 CPU 使用率,经过进一步调查,我认为这也是不正确的。
在快速查看内核代码here 后,看起来,例如,guest_nice
和 guest fields
总是与 nice
和 user
一起增加(因此它们不应包含在cpu 使用率计算,因为它们已经包含在 nice
和 user
字段中)
/*
* 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使用率的准确方法是什么,计算中应该考虑哪些字段以及如何(哪些字段归因于空闲时间,哪些字段归因于非空闲时间)?
【问题讨论】:
每秒收集CPU使用信息的正确方法是连续运行top -b
。
我想使用第 3 方脚本收集数据,而 CPU 只是需要收集的指标之一。因此,我想计算自上次运行此第 3 方脚本以来的 CPU 使用率(时间间隔可能会有所不同)。 top -b
连续运行,所以它必须在单独的线程中运行,并将收集到的数据保存在不同的输出中。
正在寻找什么 CPU 使用率?一个进程?全系统?是否应该用百分比、秒、...来表示?
上次测量的使用百分比!
【参考方案1】:
根据htop 源代码,我的假设看起来是有效的:
(见static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this)
函数LinuxProcessList.c)
// 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
第一行中列出的字段中:(请参阅documentation 的第 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
【讨论】:
我想分享一个我根据这个答案创建的小示例程序:github.com/scaidermern/top-processes。随意让我们使用它,它在 CC0 下获得许可。 我认为 iowait 是一种不间断的状态,因此将其算作“业务”而不是空闲会更实际。 guest 和 guest_nice 也应该根据这篇文章添加到 NonIdle:unix.stackexchange.com/a/303224 @KErlandsson 根据这篇文章,guest 和 guest_nice 已经添加到用户和美好时光。正如 Vangelus 回复中的评论Guest time is already accounted in usertime
【参考方案2】:
以下是基于 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
【讨论】:
【参考方案3】:嘿,我也在研究这个话题,发现这个帖子真的很有帮助。我使用 Vangelis Tasoulas 公式为此编写了一个小 Python 脚本。附件是我的 Python 代码。它每秒加载每个 cpu_id 的 cpu 使用情况。也许它也可以帮助其他人。也欢迎 cmets/建议 :-)
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
Created on 04.12.2014
@author: plagtag
'''
from time import sleep
import sys
class GetCpuLoad(object):
'''
classdocs
'''
def __init__(self, percentage=True, sleeptime = 1):
'''
@parent class: GetCpuLoad
@date: 04.12.2014
@author: plagtag
@info:
@param:
@return: CPU load in percentage
'''
self.percentage = percentage
self.cpustat = '/proc/stat'
self.sep = ' '
self.sleeptime = sleeptime
def getcputime(self):
'''
http://***.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux
read in cpu information from file
The meanings of the columns are as follows, from left to right:
0cpuid: number of cpu
1user: normal processes executing in user mode
2nice: niced processes executing in user mode
3system: processes executing in kernel mode
4idle: twiddling thumbs
5iowait: waiting for I/O to complete
6irq: servicing interrupts
7softirq: servicing softirqs
#the formulas from htop
user nice system idle iowait irq softirq steal guest guest_nice
cpu 74608 2520 24433 1117073 6176 4054 0 0 0 0
Idle=idle+iowait
NonIdle=user+nice+system+irq+softirq+steal
Total=Idle+NonIdle # first line of file for all cpus
CPU_Percentage=((Total-PrevTotal)-(Idle-PrevIdle))/(Total-PrevTotal)
'''
cpu_infos = #collect here the information
with open(self.cpustat,'r') as f_stat:
lines = [line.split(self.sep) for content in f_stat.readlines() for line in content.split('\n') if line.startswith('cpu')]
#compute for every cpu
for cpu_line in lines:
if '' in cpu_line: cpu_line.remove('')#remove empty elements
cpu_line = [cpu_line[0]]+[float(i) for i in cpu_line[1:]]#type casting
cpu_id,user,nice,system,idle,iowait,irq,softrig,steal,guest,guest_nice = cpu_line
Idle=idle+iowait
NonIdle=user+nice+system+irq+softrig+steal
Total=Idle+NonIdle
#update dictionionary
cpu_infos.update(cpu_id:'total':Total,'idle':Idle)
return cpu_infos
def getcpuload(self):
'''
CPU_Percentage=((Total-PrevTotal)-(Idle-PrevIdle))/(Total-PrevTotal)
'''
start = self.getcputime()
#wait a second
sleep(self.sleeptime)
stop = self.getcputime()
cpu_load =
for cpu in start:
Total = stop[cpu]['total']
PrevTotal = start[cpu]['total']
Idle = stop[cpu]['idle']
PrevIdle = start[cpu]['idle']
CPU_Percentage=((Total-PrevTotal)-(Idle-PrevIdle))/(Total-PrevTotal)*100
cpu_load.update(cpu: CPU_Percentage)
return cpu_load
if __name__=='__main__':
x = GetCpuLoad()
while True:
try:
data = x.getcpuload()
print data
except KeyboardInterrupt:
sys.exit("Finished")
【讨论】:
不幸的是,这似乎不准确,请在此处查看我的问题:***.com/questions/60579935/…【参考方案4】:idnt.net 对如何使用 /proc/stat cpu 数据有很好的描述,包括用于提取 cpu 的 bash 脚本和行描述。我只是想在这里链接它,因为我发现它很有价值。
【讨论】:
【参考方案5】:我也在寻找同样的东西。这是我基于 Vangelis Tasoulas 回答的 ruby 程序:
#!/usr/bin/env ruby
$VERBOSE = true
prev_file = IO.readlines(::File.join('', 'proc', 'stat')).select |line| line.start_with?('cpu')
Kernel.sleep(0.05)
file = IO.readlines(::File.join('', 'proc', 'stat')).select |line| line.start_with?('cpu')
file.size.times do |i|
data, prev_data = file[i].split.map(&:to_f), prev_file[i].split.map(&:to_f)
%w(user nice sys idle iowait irq softirq steal).each_with_index do |el, index|
eval "@#el, @prev_#el = #data[index + 1], #prev_data[index + 1]"
end
previdle, idle = @prev_idle + @prev_iowait, @idle + @iowait
totald = idle + (@user + @nice + @sys + @irq + @softirq + @steal) -
(previdle + (@prev_user + @prev_nice + @prev_sys + @prev_irq + @prev_softirq + @prev_steal))
puts "CPU #i: #((totald - (idle - previdle)) / totald * 100).round(2) %"
end
【讨论】:
【参考方案6】:以下是基于 Fidel 的回答和 arberg 的链接构建的 bash 脚本。
我想降低 cat awk grep 和 date 调用的使用率,并花费更少的 cpu 使用率来计算 cpu 使用率。
输出:
total: 4%
cpu0: 10%
cpu1: 5%
cpu2: 1%
cpu3: 1%
使用这个创建一个 bash 脚本:
#!/bin/bash
# Paramiter one used to set time in sec between reads
sleepDurationSeconds=$1
# read cpu stats to arrays
readarray -t previousStats < <( awk '/^cpu /flag=1/^intr/flag=0flag' /proc/stat )
sleep $sleepDurationSeconds
readarray -t currentStats < <( awk '/^cpu /flag=1/^intr/flag=0flag' /proc/stat )
# loop through the arrays
for i in "$!previousStats[@]"; do
# Break up arrays 1 line sting into an array element for each item in string
previousStat_elemant_array=($previousStats[i])
currentStat_elemant_array=($currentStats[i])
# Get all columns from user to steal
previousStat_colums="$previousStat_elemant_array[@]:1:7"
currentStat_colums="$currentStat_elemant_array[@]:1:7"
# Replace the column seperator (space) with +
previous_cpu_sum=$(($previousStat_colums// /+))
current_cpu_sum=$(($currentStat_colums// /+))
# Get the delta between two reads
cpu_delta=$((current_cpu_sum - previous_cpu_sum))
# Get the idle time Delta
cpu_idle=$((currentStat_elemant_array[4]- previousStat_elemant_array[4]))
# Calc time spent working
cpu_used=$((cpu_delta - cpu_idle))
# Calc percentage
cpu_usage=$((100 * cpu_used / cpu_delta))
# Get cpu used for calc cpu percentage used
cpu_used_for_calc="$currentStat_elemant_array[0]"
if [[ "$cpu_used_for_calc" == "cpu" ]]; then
echo "total: "$cpu_usage"%"
else
echo $cpu_used_for_calc": "$cpu_usage"%"
fi
done
【讨论】:
以上是关于准确计算Linux中以百分比给出的CPU使用率?的主要内容,如果未能解决你的问题,请参考以下文章