Ermitteln Sie die CPU-Auslastung pro Kern im Shell-Skript

Ermitteln Sie die CPU-Auslastung pro Kern im Shell-Skript

Ich muss die CPU-Auslastung pro Kern als Prozentsatz aus einem Shell-Skript melden, aberIch kann zB mpstat eine Sekunde lang nicht ausführen. Grundsätzlich denke ich, dass die topAnzeige der Informationen nach dem Drücken 1das ist, was ich will, aber ich kann top nicht so konfigurieren, dass dies im Batch-Modus angezeigt wird (zumindest weiß ich nicht, wie das geht). Ich könnte eine ~/.toprcDatei mit der Konfiguration erstellen, aber dann muss ich hoffen, dass die Benutzer nicht damit herumspielen.

Ich habe mir die Ausgabe angesehen mpstatund analysiert, aber sie unterstützt nur Sekunden als Intervallzeit. Mein Skript wird über SNMP aufgerufen und wenn ich 1 Sekunde auf die Antwort warte, wird ein Timeout generiert, also ist das keine Option.

Gibt es andere Möglichkeiten, die CPU-Auslastung pro Kern zu ermitteln? Ich habe über das Parsen gelesen /proc/stat, aber ich denke, das ist eher der letzte Ausweg.

Antwort1

Es gibt mehrere Möglichkeiten, eine Abfrage der CPU-Auslastung im Sekundentakt durchzuführen, entweder mithilfe eines Dienstprogramms wie dstat (Beispiel unten) oder durch direkte Abfrage von /proc/stat (Beispiel ebenfalls unten).

Lassen Sie uns die Vor- und Nachteile beider durchgehen, bevor wir zu den technischen Beispielen übergehen.

Um dstat zu verwenden, müssen Sie eine schnelle Crontab (*/1 * * * *) ausführen und das Ergebnis an eine Statistikdatei weiterleiten, die Sie überprüfen können. Der Vorteil ist, dass Ihre SNMP-Timeouts kein Problem darstellen. Der Nachteil ist, dass es nicht wirklich sofort passiert und das Ausführen der Crontab, wenn Sie diese Daten nicht wirklich suchen, Auswirkungen hat. Die Auswirkungen sind möglicherweise vernachlässigbar, aber sie sind immer noch vorhanden.

Um /proc/stat zu verwenden, müssen Sie den Inhalt von /proc/stat zweimal abfragen. Der Inhalt von /proc/stat ist kumulativ vom Start an. Die Ergebnisse der ersten und zweiten Abfrage müssen also voneinander abgezogen werden, und dann kann die Berechnung für die aktuelle Last durchgeführt werden. Der Nachteil ist, dass es bei dieser Berechnung zu einer gewissen Verzögerung kommen muss. Im folgenden Beispiel habe ich die Verzögerung auf unter eine Sekunde reduziert. Dies würde Ihren Anforderungen entsprechen, die Datenproben liegen jedoch so nah beieinander, dass ich nicht sicher bin, wie absolut genau dies ist.

Verwenden von dstat; Fügen Sie diese Zeile zu /etc/crontab hinzu:

*/1  *  *  *  *  root    echo $((100-`dstat -c -C0 --noheaders --nocolor 1 1 | grep -v "\-\|u" | awk 'NR == 2' | tr -s " " | cut -d \  -f 4`)) > /tmp/cpuload

Dies aktualisiert nur einmal pro Minute. Wenn Sie häufigere Updates wünschen, fügen Sie eine zweite Zeile hinzu und stellen Sie dem Befehl sleep 30 voran, wie

*/1  *  *  *  *   root    sleep 30; echo $((100-`dstat -c -C0 --noheaders --nocolor 1 1 | grep -v "\-\|u" | awk 'NR == 2' | tr -s " " | cut -d \  -f 4`)) > /tmp/cpuload

Es ist möglich, Cron noch weiter zu verwenden (zu missbrauchen) und Ergebnisse in weniger als einer Sekunde zu erzielen, aber das ist ein ganz anderes Thema.

Erläuterung:

dstat -c -C 0 --noheaders --nocolor 1 0

-c zeigt nur CPU-Daten

-C wähle CPU0. Nummerierung ändern, um andere CPU auszuwählen

--noheaders --nocolor (impliziert --noupdate) vereinfacht, was wir sehen

1 eine Sekunde Verzögerung beim Lesen der Statistiken

1 Beenden nach dem zweiten Lesen der Statistiken. Geben Sie ihm Zeit, sich nach dem Aufruf zu beruhigen.

grep -v "-\|u"

Nicht-Datenzeilen entfernen

awk 'NR == 2'

Wählen Sie die zweite Zeile aus.

tr -s " "

Entfernen Sie die zusätzlichen Leerzeichen, die auf dem Bildschirm zwar gut aussehen, aber nicht für die Systemnutzung geeignet sind.

Schnitt -d \ -f 4

-d \ (nach der durch \ (Escape) abgegrenzten Zeile steht ein Leerzeichen. -f 4 Leerlauf auswählen. Ja, optisch ist es 3, aber das Leerzeichen am Zeilenanfang zählt als Feld und verfälscht die Feldzählung.

$ (( ))

Bash-Rechenoperationen, wobei der Systemleerlauf von 100 abgezogen wird.

Verwenden von /proc/stat;

Speichern als cpuload.sh;

#!/bin/bash

#Calculation delay. Without a delay, there is no way to determine current 
#values. The content or /proc/stat is cumulitative from last boot.  
# in seconds; sleep must be able to support float values
dly=3

function calculate {

#load arrays
IFS=' ' read -r -a firstarr <<< "$1"
IFS=' ' read -r -a secondarr <<< "$2"

#clear name fields in array so that calculations don't get messy
firstarr[0]=0 ;
secondarr[0]=0 ;

#clear values 
firsttotcpu=0
secondtotcpu=0

#calculate the begining interrupt counts
for f in ${firstarr[@]}; 
    do 
        let firsttotcpu+=$f; 
done
firstidle=$((${firstarr[4]}+${firstarr[5]})); 

#calculate the ending interrupt counts
for l in ${secondarr[@]}; 
    do
        let secondtotcpu+=$l; 
    done; 
secondidle=$((${secondarr[4]}+${secondarr[5]})); 

#calculate the relative change counts
insttotcpu=$(( secondtotcpu - firsttotcpu ))
instidle=$(( secondidle - firstidle ))

#calculate the utilization percentage. must be done external to bash as it's a
#floating calculation
cpu_load=$( echo | awk -v tot=$insttotcpu -v idl=$instidle ' { print ( ( ( tot - idl ) / tot ) * 100 ) } ' )

echo -n $cpu_load " " 


} 
export -f calculate

#main execution

oldIFS=$IFS

IFS=$'\n' cpu_start=( $( grep cpu /proc/stat ) );

#must delay to get difference
sleep $dly

IFS=$'\n' cpu_end=( $( grep cpu /proc/stat ) );

cpucount=${#cpu_start[@]}

#uncomment this for loop to enable printing the cpu name above the percentages
#for i in ${cpu_start[@]};
#    do
#        IFS=' ' read -r -a name <<< "$i"
#        echo -n ${name[0]} " "
#done
#echo ""

for (( i=0; i<$cpucount; i++ ))
    do
        calculate "${cpu_start[$i]}" "${cpu_end[$i]}"

done

echo ""

IFS=$oldIFS

Antwort2

Eine andere Möglichkeit, die Rohwerte zu erhalten, wäre grep cpu0 /proc/stat. Dort sehen Sie die Anzahl der Ticks in jedem Zustand. man procFür die Details zur Interpretation gehen Sie wie folgt vor. Wenn Sie es als Prozentsatz haben möchten, müssen Sie sie zusammenzählen und dividieren, zum Beispiel entlang der Linien von wasJohn W. Gillschlägt vor.

Antwort3

Hier ist einBash-basiertBeispielskript (mit /proc/stat) mit Erklärungen. Es kann so schnell laufen, wie Sie es brauchen. Speichern Sie als /tmp/cpuLoad.sh, dann "chmod +x /tmp/cpuLoad.sh" und führen Sie zuletzt aus: /tmp/cpuLoad.sh

#!/bin/bash

interval=0.25; ##loop interval in seconds

##so settings below
lCpus=(); ##store last readings
lCount=0; ## loop counter

while :; do {

    cCpu=(); ##current cpu
    cCpus=(); ##all cpus
    values=$(grep -E "cpu[0-9]+\s" /proc/stat);
    for value in $values; do {
        if [[ $value =~ ^cpu[0-9]+ ]]; then
            if [[ ${#cCpu[@]} > 0 ]]; then
                cCpus[${cCpu[1]}]="${cCpu[@]}"
            fi

            cCpu[0]=$value; ##name
            cCpu[1]=${#cCpus[@]}; ##cpu index
            cCpu[2]=0; ##cpu idle ticks
            cCpu[3]=0; ##cpu busy ticks
            i=0; ## column index

        else
            ((i=i+1));
            if ([ $i == 4 ] || [ $i == 5 ]); then
                # position 4 is the idle, position 5 is the i/o wait (also idle introduced 2.5.41) src https://www.idnt.net/en-US/kb/941772
                ((cCpu[2]=cCpu[2] + value));
            else
                ((cCpu[3]=cCpu[3] + value));
            fi
        fi
    } done

    ##include the last cpu
    cCpus[${cCpu[1]}]="${cCpu[@]}"

    output="Loop $lCount";
    x=0;
    for cpu in "${cCpus[@]}"; do {
        if [[ $lCount > 0 ]]; then
        
            cCpu=($cpu);
            lCpu=(${lCpus[$x]});
            dTotal=$(((${cCpu[2]} + ${cCpu[3]}) - (${lCpu[2]} + ${lCpu[3]})));
            dUsed=$((dTotal - (${cCpu[2]} - ${lCpu[2]})));
            if [[ $dTotal == 0 ]]; then
                dTotal=1; ##dividing by 0 is never a good idea
            fi
            output="$output, ${cCpu[0]}: $((100 * dUsed / dTotal))%";
        fi
        ##store the reading so we can do a delta next round
        lCpus[$x]=$cpu;
        ((x=x+1));
        
    } done
    
    if [[ $lCount > 0 ]]; then
        echo $output;
    fi
    
    sleep $interval;
    ((lCount=lCount+1));
    
} done

Antwort4

Es stellt sich heraus, dass einige der auf RedHat installierten MIBs alle hier benötigten Informationen bereitstellen. Da mein Ziel darin besteht, diese Werte unter einer OID über SNMP bereitzustellen, kann ich SNMP nutzen und die Informationen verarbeiten.

Der Gesamt-CPU-Durchschnitt wird wie folgt berechnet 100-idle:

function allCpuLoad {
    # get system idle value from
    # snmpget -v2c -cmdaf localhost UCD-SNMP-MIB::ssCpuIdle.0
    # UCD-SNMP-MIB::ssCpuIdle.0 = INTEGER: 93
    # and compute load by substracting it from 100.0 
    snmpget -v2c -cmdaf localhost UCD-SNMP-MIB::ssCpuIdle.0|cut -f4 -d' '| awk '{printf "%d", 100 - $1}'
}

Wir können snmpwalk verwenden, um die Auslastung aller einzelnen CPUs abzurufen und dann den Maximalwert zu extrahieren:

function maxCpuLoad {
    # get load of all cpus
    # snmpwalk -v2c -cmdaf localhost HOST-RESOURCES-MIB::hrProcessorLoad
    # HOST-RESOURCES-MIB::hrProcessorLoad.196608 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196609 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196610 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196611 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196612 = INTEGER: 6
    # HOST-RESOURCES-MIB::hrProcessorLoad.196613 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196614 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196615 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196616 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196617 = INTEGER: 27
    # HOST-RESOURCES-MIB::hrProcessorLoad.196618 = INTEGER: 4
    # HOST-RESOURCES-MIB::hrProcessorLoad.196619 = INTEGER: 0
    # HOST-RESOURCES-MIB::hrProcessorLoad.196620 = INTEGER: 1
    # HOST-RESOURCES-MIB::hrProcessorLoad.196621 = INTEGER: 0
    # HOST-RESOURCES-MIB::hrProcessorLoad.196622 = INTEGER: 0
    # HOST-RESOURCES-MIB::hrProcessorLoad.196623 = INTEGER: 1
    # and get maximum value only
    snmpwalk -v2c -cmdaf localhost HOST-RESOURCES-MIB::hrProcessorLoad|cut -f 4 -d' '|sort -n -r|head -n1
}

verwandte Informationen