Obtenga carga de CPU por núcleo en el script de shell

Obtenga carga de CPU por núcleo en el script de shell

Necesito informar la carga de CPU por núcleo como porcentaje de un script de shell, peroNo puedo ejecutar, por ejemplo, mpstat durante un segundo. Básicamente, creo que lo que quiero es que la información topse muestre después de presionar 1, pero no puedo configurar la parte superior para que muestre esto en modo por lotes (al menos no sé cómo). Podría crear un ~/.toprcarchivo con la configuración, pero luego tengo que esperar que los usuarios no se metan con eso.

Miré mpstaty analicé el resultado, pero esto solo admite segundos como intervalo de tiempo. Mi secuencia de comandos se llama a través de SNMP y esperar 1 segundo por la respuesta generará un tiempo de espera, por lo que esta no es una opción.

¿Existen otras formas de obtener la carga de CPU por núcleo? Leí sobre el análisis /proc/stat, pero creo que es más un último recurso.

Respuesta1

Hay varias formas de realizar un sondeo subsegundo de la carga de la CPU, ya sea usando una utilidad, como dstat (ejemplo a continuación) o sondeando directamente /proc/stat (ejemplo también a continuación).

Repasemos los pros y los contras de ambos, antes de pasar a los ejemplos técnicos.

Para usar dstat, necesitará ejecutar un crontab rápido ( */1 * * * * ) y canalizar el resultado a un archivo de estadísticas que pueda verificar. La ventaja es que los tiempos de espera de SNMP no serán un problema; la desventaja es que no es realmente instantáneo y ejecutar el crontab cuando no estás buscando estos datos tiene un impacto. El impacto puede ser insignificante, pero aun así está ahí.

Para utilizar /proc/stat, debe sondear el contenido de /proc/stat dos veces. El contenido de /proc/stat es acumulativo desde el inicio. Por lo tanto, los resultados de la primera y la segunda encuesta deben restarse entre sí y luego se puede realizar el cálculo de la carga actual. La desventaja es que debe haber algún tipo de retraso para realizar este cálculo. En el siguiente ejemplo, reduje el retraso a menos de un segundo. Esto satisfaría sus necesidades; sin embargo, las muestras de datos están tan juntas que no estoy seguro de cuán absoluta es la precisión.

Usando dstat; Agregue esta línea a /etc/crontab:

*/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

Esto solo se actualiza una vez por minuto. Si desea actualizaciones más frecuentes, agregue una segunda línea y preceda el comando con sleep 30, como

*/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 posible usar (abusar) cron aún más y obtener resultados inferiores a un segundo, pero ese es otro tema completamente diferente.

explicación:

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

-c solo muestra datos de la CPU

-C seleccione cpu0. cambiar la numeración para seleccionar otra CPU

--noheaders --nocolor (implícito --noupdate) simplifica lo que vemos

1 retraso de un segundo en la lectura de estadísticas

1 salida después de la segunda lectura de estadísticas. Dándole tiempo para que se calme después de la invocación.

grep -v "-\|u"

eliminar líneas que no sean de datos

awk 'NR == 2'

seleccione la segunda línea.

tr -s " "

recorte los espacios adicionales que se ven bien en la pantalla pero no para uso del sistema

cortar -d \ -f 4

-d \ (hay un espacio después de la línea delineada del espacio \ (escape) -f 4 seleccione el inactivo. Sí, visualmente es 3, pero el espacio al comienzo de la línea cuenta como un campo, lo que descarta el recuento de campos.

$(( ))

operaciones aritméticas de bash, restando el sistema inactivo de 100.

Usando /proc/stat;

Guardar como 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

Respuesta2

Otra forma de obtener los valores brutos sería grep cpu0 /proc/stat. Allí verás el número de ticks en cada estado. Consulte man proclos detalles sobre la interpretación. Si lo quieres como porcentaje tienes que sumarlos y dividirlos, por ejemplo según lo queJuan W.Gillsugiere.

Respuesta3

Aquí hay unbasado en bashguión de ejemplo (usando /proc/stat) con explicaciones. puede funcionar tan rápido como lo necesites. Guarde como /tmp/cpuLoad.sh, luego "chmod +x /tmp/cpuLoad.sh" y ejecute por última vez: /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

Respuesta4

Resulta que algunas de las MIB instaladas en RedHat proporcionan toda la información necesaria aquí. Dado que mi objetivo es proporcionar estos valores bajo un OID a través de SNMP, puedo utilizar SNMP y procesar la información.

El promedio de toda la CPU se calcula como 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}'
}

Podemos usar snmpwalk para obtener la carga de todos los CPU individuales y luego extraer el valor máximo:

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
}

información relacionada