シェルスクリプトでコアごとのCPU負荷を取得する

シェルスクリプトでコアごとのCPU負荷を取得する

シェルスクリプトからコアごとのCPU負荷をパーセンテージで報告する必要があるのですが、たとえばmpstatを1秒間実行できない基本的に、押した後に情報topが表示されるの1が私の希望だと思いますが、バッチ モードでこれを表示するように top を構成することはできません (少なくとも方法がわかりません)。~/.toprc構成を含むファイルを作成することはできますが、ユーザーがそれを変更しないことを祈るしかありません。

出力を確認しmpstatて解析しましたが、間隔時間として秒のみがサポートされています。スクリプトは SNMP 経由で呼び出され、応答を 1 秒待つとタイムアウトが生成されるため、これはオプションではありません。

コアごとの CPU 負荷を取得する他の方法はありますか? 解析について読みました/proc/statが、これは最後の手段だと思います。

答え1

CPU 負荷の 2 次ポーリングを実行するには、dstat などのユーティリティを使用する (以下の例) か、/proc/stat を直接ポーリングする (以下の例も参照) など、いくつかの方法があります。

技術的な例に進む前に、両方の長所と短所を確認しましょう。

dstat を使用するには、高速 crontab( */1 * * * * ) を実行し、その結果を統計ファイルにパイプして確認する必要があります。利点は、SNMP タイムアウトが問題にならないことです。欠点は、完全に瞬時ではないことと、実際にこのデータを探していないときに crontab を実行すると影響が出ることです。影響はごくわずかかもしれませんが、それでも影響はあります。

/proc/stat を使用するには、/proc/stat の内容を 2 回ポーリングする必要があります。/proc/stat の内容は、起動時から累積されます。したがって、最初のポーリング結果と 2 番目のポーリング結果を減算してから、現在の負荷を計算する必要があります。欠点は、この計算を行うために何らかの遅延が必要になることです。以下の例では、遅延を 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分ごとに1回だけ更新されます。より頻繁に更新したい場合は、2行目を追加し、コマンドの前に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秒の遅延

統計の 2 回目の読み取り後に 1 回終了します。呼び出し後に落ち着くまでの時間を与えます。

grep -v "-\|u"

非データ行を削除する

awk 'NR == 2'

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

がここにありますbashベースサンプルスクリプト(/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
}

関連情報