[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[plamo:34362] Re: crontab内での環境変数設定



川俣です。

cronの環境変数の話がずっと続いてますが、気分転換にちょっと寄り道を。

今回の話の発端は、私が開発しているSAG (System Activity Grapher)をLinux
に移植しようとしたことですが、このSAGのcronを使わない実装を考えてみま
した。

SAGは、一定間隔でシステムから取得したデータを取得・記録し、グラフ形式
で描画するソフトです。実際のグラフはこんな感じです。

    https://fuguita.org/sag/

一定間隔の処理をするためにcronを使っているわけですが、そのcrontabはこ
んな感じです:

    # ### for System Activity Grapher logging system ###
    #
    # $Id: crontab,v 1.2 2017/01/07 06:10:54 kaw Exp $
    #
    #
    SHELL=/bin/sh
    PATH=/bin:/sbin:/usr/bin:/usr/sbin
    #
    # Important - base directory of SAG
    # Reconfigure if needed.
    #
    SAGHOME=/home/sag
    #
    #minute	hour	mday	month	wday	command
    #
    *	*	*	*	*	exec $SAGHOME/bin/tick
    */5	*	*	*	*	chmod u+x $SAGHOME/bin/t0005
    50	*	*	*	*	chmod u+x $SAGHOME/bin/t0100
    10	4	*	*	*	chmod u+x $SAGHOME/bin/t2400

このcrontabの最後から4行目にあるように、tickスクリプトが毎分実行されま
す。

t0005, t0100, t2400は、5分、1時間、24時間毎に実行されるデータ取得・
処理スクリプトですが、cronではこれらを直接実行せず、実行属性を与えるだ
けになっています

で、これらのスクリプトはtickの中で実行されるのですが、実際には以下のよ
うになっています。

    #!/bin/sh

    #  tick  -  driver for t0001, t0005 ...
    #
    #  $Id: tick,v 1.8 2021/10/05 02:29:26 kaw Exp $

    cd $SAGHOME || exit 1

    sleep 10  # to exec after t[0-9][0-9][0-9][0-9]

    cmdseq='/usr/bin/nice -n 15 /bin/sh bin/t0001'
    for sh in bin/t[0-9][0-9][0-9][0-9]
    do
        if [ -x $sh ]
        then
            cmdseq="$cmdseq; /usr/bin/nice -n 15 /bin/sh $sh"
            chmod a-x $sh
        fi
    done

    export DAYTIME=$(date '+%Y %m %d %H %M')
    eval $cmdseq &

ご覧のように実行属性の付いたスクリプトを低優先度、かつ、シーケンシャル
に実行するようにしています。

低優先度にするのは、システムの本来の動作になるべく影響を及ぼさないよう
に、シーケンシャルにするのは、crontabから直接実行すると、t????のスクリ
プトが並行動作となり、これまた負荷が増える原因になるので、それを防ぐた
めです。
(あと、t????に依存関係がある場合に各スクリプトが干渉しないようにもして
います(現状ではt????のスクリプト間に依存関係はないですが....))。

さて、これをcronを使わずに実装するにはどうすればいいか?

基本的なアイデアはとても単純で、毎分実行されるループを走らせ、ループの
内部で、その時刻に該当するスクリプトを走らせればいいだけです。

    #!/bin/sh

    while :;  # 無限ループ
    do
        時刻を取得
        t0001
        if 5分間隔   ; then t0005; fi
        if 1時間間隔 ; then t0100; fi
        if 1日間隔   ; then t2400; fi
        sleep 60
    done

こんな感じですね。

でも、このスクリプトには基本的な欠陥があります。

この無限ループを実行すると、ループの実行間隔は処理スクリプトの実行時間
分、60秒より少しだけ長くなります。
ということは、そのうち、処理されない分が出て来ます(例えば、15分の次が
17分になって16分がとばされる、など)。

それを防ぐために、次のような実装にしてみました。

この実装では、スクリプトは2つに分割されています。
無限ループの本体部分 sag_ticker.sh と,
その中で実行される部分 sag_driver.sh です。

  [sag_ticker.sh]
    #!/bin/sh

    export SAGHOME=/home/kaw/sag
    cd "$SAGHOME" || exit 1

    min=$(date +%S)  # 分を取得
    min=${min#0}     # 先頭の0を削除

    sleep $((60 + 30 - min))  # 次の分の30秒まで待つ

    while :;
    do
        sleep $(./bin/sag_driver.sh)
    done 2>&1 | ts > sag.log &  # エラー等を日時付きで記録

  [sag_driver.sh]
    #!/bin/sh

    export DAYTIME=$(date +'%Y %m %d %H %M')  # 現在時刻:処理プロセスで使用
    set -- $DAYTIME
    hour="${4#0}"  # 先頭の0を削除
     min="${5#0}"  # 先頭の0を削除

    commands="./bin/t0001"

    [[ $((min % 5)) = 1 ]]       && commands="${commands}; ./bin/t0005"
    [[ "$min" = 50 ]]            && commands="${commands}; ./bin/t0100"
    [[ "$hour:$min" = "23:50" ]] && commands="${commands}; ./bin/t2400"

    nice -n 15 /bin/sh -c "$commands" &  # 低優先度で処理を実行

    echo $((60 + 30 - $(date +%S)))  # 次の分の30秒まで待つ秒数を返す

このスクリプトのキモは、処理プロセスが必ず毎分30秒から実行されるように
なっていることです。

これで、cronに依存しない実装ができました :-)

最終的には、インストーラでcron版/スタンドアロン版を選択できるようにし
ようと思っています。

SAGプロジェクトページ: https://fuguita.org/?SAG
開発リポジトリ: https://github.com/ykaw/sag

----
川俣 吉広
mailto:kaw@xxxxxxxxxxxx
https://fuguita.org/


References
[plamo:34333] crontab内での環境変数設定, Yoshihiro Kawamata

[検索ページ] [メール一覧]
Plamo ML 公開システム