[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[plamo:34362] Re: crontab内での環境変数設定
-
From:Yoshihiro Kawamata
-
Date:Mon, 15 Jul 2024 14:44:04 +0900 (JST)
- Subject: [plamo:34362] Re: crontab内での環境変数設定
- From: "Yoshihiro Kawamata" <kaw@xxxxxxxxxxxx>
- Date: Mon, 15 Jul 2024 14:44:03 +0900 (JST)
川俣です。
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 公開システム