LoginSignup
9
8

More than 5 years have passed since last update.

cron から起動されたジョブを、一時停止・再開する

Last updated at Posted at 2014-10-20

我が家は伊豆の山奥なので光回線が来ておらず、遅い ADSL でやりくりをしています。一方、サーバは cron(8) で rsync(1) を起動してリモートからのファイルのミラーリングをしているのですが、こいつがネットワークの帯域を専有してしまうと他の作業ができなくなってしまいます。そこで、cron から実行されたコマンドを必要に応じて一時停止・再開できると良いなと思った次第です。

(この手のデーモン化の手段は、Linux なんかでは普通に提供されていたと思うんですが、Mac でも動かしたかったのと、pid ファイルやロックファイルを使いたくなかったので、以下のような書き方になっている次第です)

以下はサンプルです。putdates0 exec で、端末に延々と日付を表示し始めます。他の端末から putdates0 stop とすればプロセスは一時停止、putdates0 cont とすれば再開、putdates0 snooze とすればプロセスは一時停止した後 10 秒後に再開します。

putdates0
#!/bin/bash
# -*- coding: utf-8 -*-

## プロセスの名前(システムでユニークに)
process_name="SimplePutDatesProcess"

## スヌーズさせた際の復帰までの秒数
snooze_interval="10"

## サブコマンド: exec | term | stop | cont | snooze | _doit
subcommand="$1"

pid=$(pgrep -f "^$process_name ")

## 子孫プロセスを一覧する
function get_children() {
    for pid in $(pgrep -P $1)
    do
        echo $pid
        get_children $pid
    done
}

## 子孫プロセスにシグナルを発行する
function kill_tree() {
    signal=$1
    pid=$2
    kill -s $signal $pid $(get_children $pid)
}

## サブコマンドの処理
case $subcommand in
    ## プロセス名を変更するために、実際の仕事をするプロセスを exec する
    exec )
        if test -z "$pid"
        then
            exec -a "$process_name" bash "$0" _doit
        fi
        exit 0
        ;;
    ## プロセスの終了
    term )
        kill_tree TERM $pid
        ;;
    ## プロセスの一時停止
    stop )
        kill_tree STOP $pid
        ;;
    ## プロセスの再開
    cont )
        kill_tree CONT $pid
        ;;
    ## プロセスを一時停止するが、一定時間後に再開
    snooze )
        kill_tree STOP $pid
        (
            sleep $snooze_interval
            bash "$0" cont
        ) &
        ;;
    ## 実際のジョブ
    _doit )
        while true
        do
            date
            sleep 1
        done
        ;;
esac

同種のコマンドを作成する際に上記コードをコピペしたくはありません。下記のように、実行コマンド(putdates)と実ジョブコマンド(_putdates)に実装を分けて、プロセス管理の機能は smartprocess の名前で外出ししました。putdates stat のように、プロセスの状態を確認するサブコマンドを追加しています。

putdates
#!/bin/bash
# -*- coding: utf-8 -*-

exec smartprocess _putdates PutDatesProcess 3600 "$@"
_putdates
#!/bin/bash
# -*- coding: utf-8 -*-

while true
do
    date
    sleep 1
done
smartprocess
#!/bin/bash
# -*- coding: utf-8 -*-

job_command="$1"
process_name="$2"
snooze_interval="$3"
subcommand="$4"

pid=$(pgrep -f "^$process_name ")

## 子孫プロセス一覧
function get_children() {
    for pid in $(pgrep -P $1)
    do
        echo $pid
        get_children $pid
    done
}

## 子孫プロセスにシグナルを発行
function kill_tree() {
    signal=$1
    pid=$2
    kill -s $signal $pid $(get_children $pid)
}

## サブコマンドの処理
case $subcommand in
    ## 実際の仕事をするプロセスを exec する
    exec )
        if test -z "$pid"
        then
            exec -a "$process_name" bash "$0" "${@:1:$#-1}" _doit
        fi
        exit 0
        ;;
    ## プロセスの終了
    term )
        kill_tree TERM $pid
        ;;
    ## プロセスの一時停止
    stop | sus | susp | suspend )
        kill_tree STOP $pid
        ;;
    ## プロセスの再開
    cont | res | resume )
        kill_tree CONT $pid
        ;;
    ## プロセスを一時停止するが、一定時間後に再開
    snooze )
        kill_tree STOP $pid
        (
            sleep $snooze_interval
            bash "$0" "${@:1:$#-1}" cont
        ) &
        ;;
    ## 実際のジョブプロセスを、排他で起動
    _doit )
        $job_command
        ;;
    ## プロセスの状態を表示
    stat )
        if test -z "$pid"
        then
            echo Not Running
            exit 1
        fi
        statline=$(ps uww $pid | tail -n 1)
        stat=$(echo "$statline" | awk '{print $8}')
        echo $stat | grep -q -e T && state=Stopped || state=Running
        started=$(echo "$statline" | awk '{print $9}')
        time=$(echo "$statline" | awk '{print $10}')
        echo pid: $pid \| started: $started \| time: $time \| state: $state
        ;;
esac

このような感じで、実行コマンドと、時間のかかる実ジョブコマンドを用意すれば、いつでもジョブの一時停止と再開ができるようになります。

crontab(5) の設定的には、一定間隔で exec をしておき、stop 後の cont のし忘れに対処するために、一日に一度ほど cont するようにしておくと良いのではないでしょうか。

今のところ動作は良好なようで、Mac OS X、Linux 上で共に動作しています。何かお気づきの点がありましたらコメントをいただければと思います。

ではまた。

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8