Es ist eine Frage, die oft gestellt wurde, aber es gibt keine fundierte Antwort, die ich finden könnte.
Viele Leute schlagen die Verwendung des Befehls top vor, aber wenn Sie top einmal ausführen (weil Sie ein Skript haben, das beispielsweise alle 1 Sekunde die CPU-Auslastung erfasst), wird immer das gleiche Ergebnis für die CPU-Auslastung angezeigt ( 1 , Beispiel 2 ).
Eine genauere Methode zur Berechnung der CPU-Auslastung ist das Lesen der Werte aus /proc/stat
. Die meisten Antworten verwenden jedoch nur die ersten 4 Felder aus /proc/stat
, Um sie zu berechnen (ein Beispiel hier ).
/proc/stat/
Hat ab Linux-Kernel 2.6.33 10 Felder pro CPU-Kern!
Ich fand auch diese Frage zur genauen Berechnung der CPU-Auslastung unter Linux mit/proc/stat , die auf dasselbe Problem hinweist, das die meisten anderen Fragen nur berücksichtigen Überlegung 4 von den vielen Feldern - aber die hier gegebene Antwort beginnt mit "Ich denke" (nicht sicher), und außer dass es nur um die ersten 7 Felder geht (von 10 in /proc/stat/
)
Dieses Perl-Skript verwendet alle Felder, um die CPU-Auslastung zu berechnen, was nach einigen weiteren Untersuchungen meines Erachtens ebenfalls nicht korrekt ist.
Nach einem kurzen Blick in den Kernel-Code hier sieht es zum Beispiel so aus, als wären guest_Nice
Und guest fields
Wird immer zusammen mit Nice
und user
erhöht (daher sollten sie nicht in die Berechnung der CPU-Auslastung einbezogen werden, da sie bereits in den Feldern Nice
und user
enthalten sind )
/*
* 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;
}
}
Zusammenfassend lässt sich sagen, wie die CPU-Auslastung unter Linux genau berechnet werden kann und welche Felder in den Berechnungen berücksichtigt werden sollten und wie (welche Felder werden der Leerlaufzeit und welche Felder der Nicht-Leerlaufzeit zugeordnet).
Laut dem htop Quellcode zum Zeitpunkt des Schreibens, scheinen meine Annahmen gültig zu sein:
(Siehe void ProcessList_scan(ProcessList* this)
function at ProcessList.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...)
idlealltime = idletime + ioWait; # ioWait is added in the idleTime
systemalltime = systemtime + irq + softIrq;
virtalltime = guest + guestnice;
totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
Und so aus den Feldern in der ersten Zeile von /proc/stat
: (siehe Abschnitt 1.8 unter Dokumentation )
user Nice system idle iowait irq softirq steal guest guest_Nice
cpu 74608 2520 24433 1117073 6176 4054 0 0 0 0
Algorithmisch können wir den Prozentsatz der CPU-Auslastung wie folgt berechnen:
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
Das Folgende ist ein Bash-Skript, das auf der Antwort von Vangelis basiert. Es erzeugt eine Ausgabe wie diese:
total 49.1803
cpu0 14.2857
cpu1 100
cpu2 28.5714
cpu3 100
cpu4 30
cpu5 25
Erstelle eine Datei mit dem Namen get_cpu_usage.sh
Führen Sie es mit dem folgenden Befehl aus: bash get_cpu_usage.sh 0.2
Das Argument ist die Anzahl der zu messenden Sekunden. In diesem Fall sind es 200 Millisekunden.
Die Inhalte sind:
#!/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
Hey, ich habe auch nach dem Thema gesucht und fand diesen Thread wirklich hilfreich. Ich habe die Vangelis Tasoulas-Formel verwendet, um ein kleines python Skript dafür zu schreiben. Im Anhang befindet sich mein Python Code für das Problem. Es lädt jede Sekunde die CPU-Auslastung pro CPU_ID Vielleicht hilft es auch anderen. Auch Kommentare/Vorschläge sind willkommen :-)
#!/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://stackoverflow.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")
idnt.net hat eine gute Beschreibung für die Verwendung der/proc/stat-CPU-Daten, enthält ein Bash-Skript zum Extrahieren der CPU und eine Beschreibung der Zeilen. Ich wollte es nur hier verlinken, da ich es wertvoll fand.
#!/usr/bin/Ruby -w
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