Obtenha carga de CPU por núcleo em script de shell

Obtenha carga de CPU por núcleo em script de shell

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 topque 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 ~/.toprcarquivo com a configuração, mas aí tenho que torcer para que os usuários não mexam nisso.

Observei mpstate 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 procpara 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
}

informação relacionada