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 top
se 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 ~/.toprc
archivo con la configuración, pero luego tengo que esperar que los usuarios no se metan con eso.
Miré mpstat
y 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 proc
los 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
}