Preciso relatar a carga da CPU por núcleo como uma porcentagem de um script de shell, masNão consigo executar, por exemplo, mpstat por um segundo. Basicamente acho que as informações top
que aparecem após pressionar 1
é o que eu quero, mas não consigo configurar o top para mostrar isso em lote (pelo menos não sei como). Eu poderia criar um ~/.toprc
arquivo com a configuração, mas aí tenho que torcer para que os usuários não mexam nisso.
Observei mpstat
e analisei a saída, mas ela suporta apenas segundos como intervalo de tempo. Meu script é chamado via SNMP e esperar 1s pela resposta gerará um tempo limite, portanto isso não é uma opção.
Existem outras maneiras de obter a carga da CPU por núcleo? Eu li sobre parsing /proc/stat
, mas acho que este é mais um último recurso.
Responder1
Existem várias maneiras de realizar a pesquisa sub-sccond da carga da CPU, usando um utilitário, como dstat, (exemplo abaixo) ou pesquisando diretamente /proc/stat, (exemplo também abaixo).
Vejamos os prós e os contras de ambos, antes de passarmos aos exemplos técnicos.
Para usar o dstat, você precisará executar um crontab rápido ( */1 * * * * ) e canalizar o resultado para um arquivo de estatísticas que você pode verificar. A vantagem é que os tempos limite do SNMP não serão um problema; a desvantagem é que não é realmente instantâneo e executar o crontab quando você não está realmente procurando por esses dados tem um impacto. O impacto pode ser insignificante, mas ainda assim existe.
Para usar /proc/stat, você deve pesquisar o conteúdo de /proc/stat duas vezes. O conteúdo de /proc/stat é cumulativo desde a inicialização. Portanto, os resultados da primeira e da segunda pesquisa precisam ser subtraídos um do outro e então o cálculo da carga atual pode ser feito. A desvantagem é que deve haver algum tipo de atraso para fazer esse cálculo. No exemplo abaixo, reduzi o atraso para menos de um segundo. Isso atenderia às suas necessidades, no entanto, as amostras de dados estão tão próximas que não tenho certeza de quão absoluta é a precisão.
Usando dstat; Adicione esta linha ao /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
Isso é atualizado apenas uma vez a cada minuto. Se você quiser atualizações mais frequentes, adicione uma segunda linha e prefacie o comando com 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
É possível usar (abusar) o cron ainda mais longe e obter resultados abaixo de um segundo, mas isso é outro tópico completamente.
explicação:
dstat -c -C 0 --noheaders --nocolor 1 0
-c mostra apenas dados da CPU
-C selecione CPU0. altere a numeração para selecionar outra CPU
--noheaders --nocolor (implícito --noupdate) simplifica o que vemos
1 atraso de um segundo na leitura das estatísticas
1 saída após a segunda leitura das estatísticas. Dando tempo para se acalmar após a invocação.
grep -v "-\|você"
remover linhas que não sejam de dados
awk 'NR == 2'
selecione a segunda linha.
tr -s " "
corte os espaços extras que ficam bem na tela, mas não para uso do sistema
cortar -d \ -f 4
-d \ (há um espaço após a linha delineada pelo espaço \ (escape) -f 4 selecione o inativo. sim, é visualmente 3, mas o espaço no início da linha conta como um campo, eliminando a contagem de campos.
$ (( ))
operações aritméticas bash, subtraindo o sistema ocioso de 100.
Usando /proc/stat;
Salvar 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
Responder2
Outra maneira de obter os valores brutos seria grep cpu0 /proc/stat
. Lá você vê o número de ticks em cada estado. Faça isso man proc
para obter detalhes sobre a interpretação. Se você quiser como uma porcentagem, terá que somá-los e dividir, por exemplo, de acordo com o queJohn W. Gillsugere.
Responder3
Aqui está umbaseado em bashscript de exemplo (usando /proc/stat) com explicações. ele pode funcionar tão rápido quanto você precisar. Salve como /tmp/cpuLoad.sh, depois "chmod +x /tmp/cpuLoad.sh" e última execução: /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
Responder4
Acontece que alguns dos MIBs instalados no RedHat fornecem todas as informações necessárias aqui. Como meu objetivo é fornecer esses valores em um OID via SNMP, posso utilizar o SNMP e processar as informações.
A média de toda a CPU é calculada 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 obter a carga de todas as CPUs individuais e então extrair o 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
}