2009年3月7日

解答を書いておく -3- 部分解の2

request2


meminfo.awk が存在する、と言う前提からはじめる。

SLAB=$(( awk -f meminfo.awk /proc/meminfo >> $LOGFILE ) 2>&1 )

で $SLAB に現在の /proc/meminfo 中のSLABの値が取得できているとしよう。他に次のような値を bash スクリプトレベルで取得、定義できているとする。

# We only have 876Mbytes of memory space for kernel.
KERNELMEMORY=$( expr 876*1024 )
PREVIOUSSTATE="GREEN"

# 358809kbyte is 40% of 876*1024Kbytes
YELLOWBORDERSIZE=358809

# or define this.
# YELLOWLIMIT=0.4

# 627916kbyte is 70% of 876*1024Kbytes
REDBORDERSIZE=627916

# or define this.
# REDLIMIT=0.7

KERNELMEMORY はコメントにあるとおり。32bit モードの Linux は 876Mbyteしかカーネル用空間が無いのでこれが利用できる最大値。

PREVIOUSSTATEは「前回計算した際のステート」。ステートは GREEN, YELLOW, RED のどれかしかないとし、全部文字列で表すことにする。

YELLOWBORDERSIZE は Slab がこの値を超えていたらステートを YELLOW として出力する値。
YELLOWLIMIT は KERNELMEMORY サイズにこの値を乗じて得られた結果を YELLOWBORDERSIZE として用いる、という値。
もし、YELLOWLIMIT と YELLOWBORDERSIZE の両方が定義されていたらどちらか小さい方を採択する。
片方がそんざいしてもう一方が存在しないなら、存在する方が有効になる。どちらも存在しなければ YELLOWBORDERSIZE=358809 が定義されているものとして処理する。

REDBORDERSIZE は Slab がこの値を超えていたらステートを RED として出力する値。
REDLIMIT は KERNELMEMORY サイズにこの値を乗じて得られた結果を REDBORDERSIZE として用いる、という値。
もし、REDLIMIT と REDBORDERSIZE の両方が定義されていたらどちらか小さい方を採択する。
片方がそんざいしてもう一方が存在しないなら、存在する方が有効になる。どちらも存在しなければ REDBORDERSIZE=627916 が定義されているものとして処理する。

もし、YELLOWLIMIT や YELLOWBORDERSIZE が何らかの理由で REDLIMIT や REDBORDERSIZE よりも大きい場合は、自動的に REDLIMIT, REDBORDERSIZE と同じ値にまで下がったものとみなす。

何らかの理由でREDLIMIT, REDBORDERSIZE が KERNELMEMORY 以上の値になった場合は、REDBORDERSIZE=$KERNELMEMORY と見なす。これが意味のある制約かどうかは微妙だが…。

YELLOWLIMIT, YELLOWBORDERSIZE が 0以下の場合はデフォルト値が採択される。

REDLIMIT, REDBORDERSIZE が 0以下の場合はデフォルト値が採択される。



これで判るとおり、実は境界問題はかなり厄介な問題をはらんでいる。与える境界値が適切な場合は特に問題ないのだが、境界値が異常値の場合、妥当に動かすのは著しく困難を伴う。その最大の理由は、プログラマが妥当な状態を決めなくてはいけないからだ。

この問題の本当の難しいところは、条件判断ではない。

妥当ではない計算条件を与えられたときに、それをいかにして跳ね除けるか、という点だ。



さて、プログラムだ。浮動小数点演算を bash で実行するのは難しいので、awk で実装する。いくつかの定数については、awk側に埋め込む。残りの引数は gawk の -v オプションを用いて引き渡す。

CalculateStatus.awk
#! /bin/gawk -f
BEGIN {
KERNELMEMORY = 876 * 1024;
DefaultYELLOWBORDERSIZE = 358809;
DefaultREDBORDERSIZE = 627916;
GreenName = "GREEN";
YellowName = "YELLOW";
RedName = "RED";


# First, calucate PreYELLOWBORDERSIZE without thinking about RED.
if ( YELLOWLIMIT > 0 ) {
PreYELLOWBORDERSIZE = KERNELMEMORY * YELLOWLIMIT;
} else {
PreYELLOWBORDERSIZE = 0;
}
if ( YELLOWBORDERSIZE > 0 ) {
if ( PreYELLOWBORDERSIZE > 0 ) {
if ( PreYELLOWBORDERSIZE > YELLOWBORDERSIZE ) {
PreYELLOWBORDERSIZE = YELLOWBORDERSIZE;
}
} else {
PreYELLOWBORDERSIZE = YELLOWBORDERSIZE;
}
} else {
PreYELLOWBORDERSIZE = DefaultYELLOWBORDERSIZE;
}

# First, calucate PreREDBORDERSIZE without thinking about YELLOW.
if ( REDLIMIT > 0 ) {
PreREDBORDERSIZE = KERNELMEMORY * REDLIMIT;
} else {
PreREDBORDERSIZE = 0;
}
if ( REDBORDERSIZE > 0 ) {
if ( PreREDBORDERSIZE > 0 ) {
if ( PreREDBORDERSIZE > REDBORDERSIZE ) {
PreREDBORDERSIZE = REDBORDERSIZE;
}
} else {
PreREDBORDERSIZE = rEDBORDERSIZE;
}
} else {
PreREDBORDERSIZE = DefaultREDBORDERSIZE;
}

# OK. Re-calculate YELLOW according to RED.
if ( PreREDBORDERSIZE < PreYELLOWBORDERSIZE ) {
PreYELLOWBORDERSIZE = PreREDBORDERSIZE;
}

YELLOWBORDERSIZE = PreYELLOWBORDERSIZE;
REDBORDERSIZE = PreREDBORDERSIZE;

if ( SLAB > REDBORDERSIZE ) {
NewStatus = RedName;
} else if ( Slab > YELLOWBORDERSIZE ) {
NewStatus = YellowName;
} else {
NewStatus = GreenName;
}

# Ok. check old status in mind.
if ( PREVIOUSSTATE == RedName ) {
NewStatus = RedName;
} else if (( PREVIOUSSTATE == YellowName )&&( NewStatus = GreenName )) {
NewStatus = YellowName;
}

print NewStatus;
exit 0;
}

結果は stdout で出て行くので、bash 変数に取り込めばよい。