쉘 스크립트에서 코어당 CPU 로드 가져오기

쉘 스크립트에서 코어당 CPU 로드 가져오기

쉘 스크립트에서 코어당 CPU 로드를 백분율로 보고해야 하지만예를 들어 mpstat를 1초 동안 실행할 수 없습니다.. 기본적으로 나는 누른 후에 정보가 top표시되는 것이 1내가 원하는 것이라고 생각하지만 배치 모드에서 이것을 표시하도록 top을 구성할 수는 없습니다(적어도 방법은 모르겠습니다). 구성을 사용하여 파일을 만들 수 있지만 ~/.toprc사용자가 해당 구성을 망치지 않기를 바랍니다.

출력을 보고 mpstat구문 분석했지만 이는 간격 시간으로 초만 지원합니다. 내 스크립트는 SNMP를 통해 호출되며 응답을 1초 동안 기다리면 시간 초과가 발생하므로 이는 옵션이 아닙니다.

코어당 CPU 로드를 얻는 다른 방법이 있습니까? 나는 parsing 에 대해 읽었 /proc/stat지만 이것이 최후의 수단이라고 생각합니다.

답변1

dstat(아래 예)와 같은 유틸리티를 사용하거나 /proc/stat(아래 예)을 직접 폴링하여 CPU 로드의 하위 sccond 폴링을 수행하는 방법에는 여러 가지가 있습니다.

기술적인 예제를 진행하기 전에 두 가지의 장단점을 살펴보겠습니다.

dstat를 사용하려면 빠른 crontab( */1 * * * * )을 실행하고 결과를 확인할 수 있는 통계 파일로 파이프해야 합니다. 장점은 SNMP 시간 초과가 문제가 되지 않는다는 것입니다. 단점은 실제로 즉각적이지 않으며 실제로 이 데이터를 찾고 있지 않을 때 crontab을 실행하는 것이 영향을 미친다는 것입니다. 그 영향은 미미할 수도 있지만 여전히 존재합니다.

/proc/stat를 사용하려면 /proc/stat의 내용을 두 번 폴링해야 합니다. /proc/stat의 내용은 부팅 시 누적됩니다. 따라서 첫 번째 폴링과 두 번째 폴링 결과를 서로 빼야 현재 부하에 대한 계산이 이루어질 수 있습니다. 단점은 이 계산을 수행하는 데 어떤 형태의 지연이 있어야 한다는 것입니다. 아래 예에서는 지연 시간을 1초 미만으로 줄였습니다. 이는 귀하의 요구 사항을 충족하지만 데이터 샘플이 너무 가까워서 정확도가 얼마나 절대적인지 잘 모르겠습니다.

dstat 사용; /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

이는 1분에 한 번만 업데이트됩니다. 더 자주 업데이트하려면 두 번째 줄을 추가하고 다음과 같이 명령 앞에 sleep 30을 추가하세요.

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

cron을 더 멀리 사용(남용)하여 1초 미만의 결과를 얻을 수도 있지만 이는 완전히 다른 주제입니다.

설명:

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

-c CPU 데이터만 표시

-C CPU0을 선택합니다. 다른 CPU를 선택하려면 번호 매기기를 변경하세요.

--noheaders --nocolor (묵시적 --noupdate) 우리가 보는 것을 단순화합니다.

1 통계 판독 시 1초 지연

두 번째 통계 읽기 후 1회 종료. 호출 후 안정될 시간을 줍니다.

grep -v "-\|u"

비 데이터 라인 제거

awk 'NR == 2'

두 번째 줄을 선택합니다.

tr -s " "

화면에서는 보기 좋지만 시스템 사용에는 적합하지 않은 추가 공간을 잘라냅니다.

잘라내기 -d \ -f 4

-d \ (\(이스케이프) 공백 설명 줄 뒤에 공백이 있습니다. -f 4 유휴 상태를 선택합니다. 예, 시각적으로 3이지만 줄 시작 부분의 공백은 필드로 계산되어 필드 수를 버립니다.

$ (( ))

bash 산술 연산, 100에서 시스템 유휴 상태를 뺍니다.

/proc/stat 사용;

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

답변2

원시 값을 얻는 또 다른 방법은 grep cpu0 /proc/stat. 각 상태의 틱 수가 표시됩니다. man proc해석에 대한 자세한 내용을 확인 하세요 . 백분율로 표시하려면 이를 합산하여 나누어야 합니다. 예를 들어 다음과 같은 선을 따라 나누어야 합니다.존 W. 길제안.

답변3

여기에는배쉬 기반예제 스크립트(/proc/stat 사용) 설명과 함께. 필요한 만큼 빠르게 실행될 수 있습니다. /tmp/cpuLoad.sh로 저장한 다음 "chmod +x /tmp/cpuLoad.sh"를 입력하고 마지막으로 실행: /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

답변4

RedHat에 설치된 일부 MIB는 여기에 필요한 모든 정보를 제공하는 것으로 나타났습니다. 내 목표는 SNMP를 통해 OID에 이러한 값을 제공하는 것이므로 SNMP를 활용하고 정보를 처리할 수 있습니다.

전체 CPU 평균은 다음과 같이 계산됩니다 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}'
}

snmpwalk를 사용하여 모든 개별 CPU의 로드를 가져온 다음 최대값을 추출할 수 있습니다.

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
}

관련 정보