96

任意の汎用スクリプトまたはコマンドをデーモンに変換できるデーモン化ツールが欲しいです。

私が対処したい2つの一般的なケースがあります:

  1. 永久に実行する必要があるスクリプトがあります。停止した場合 (または再起動時) は、再起動します。一度に 2 つのコピーが実行されないようにします (コピーが既に実行されているかどうかを検出し、その場合は起動しません)。

  2. 簡単なスクリプトまたはコマンド ライン コマンドを永久に繰り返し実行したい (実行の間に短い休止を挟む) 場合があります。繰り返しますが、スクリプトの 2 つのコピーを同時に実行することはできません。

もちろん、ケース 2 のスクリプトの周りに "while(true)" ループを記述し、ケース 1 のソリューションを適用するのは簡単ですが、より一般的なソリューションは、ケース 1 のスクリプトに適用されるため、ケース 2 を直接解決するだけです。まあ(スクリプトが死ぬことを意図していない場合は、一時停止を短くするか、一時停止しないでください(もちろん、スクリプトが実際に死ぬことがない場合、一時停止は実際には問題になりません))。

ソリューションには、たとえば、既存のスクリプトにファイル ロック コードや PID 記録を追加することは含まれないことに注意してください。

より具体的には、次のように実行できる「デーモン化」プログラムが欲しい

% daemonize myscript arg1 arg2

または、たとえば、

% daemonize 'echo `date` >> /tmp/times.txt'

これにより、times.txt に追加された日付のリストが増え続けます。(デーモン化する引数が上記のケース 1 のように永久に実行されるスクリプトである場合、デーモン化は引き続き正しいことを行い、必要に応じて再起動します。) 次に、上記のようなコマンドを .login に入れることができます。および/または1時間ごとまたは1分ごとにcronで実行します(予期せず死ぬことをどれだけ心配しているかによって異なります)。

注意: デーモン化スクリプトは、デーモン化するコマンド文字列を覚えておく必要があります。これにより、同じコマンド文字列が再度デーモン化された場合に、2 番目のコピーが起動されなくなります。

また、このソリューションは理想的には OS X と Linux の両方で動作するはずですが、どちらか一方のソリューションも歓迎します。

編集: で呼び出す必要がある場合は問題ありませんsudo daemonize myscript myargs

(私がこれをすべて間違っていると考えている場合、または簡単で汚い部分的な解決策がある場合は、それも聞いてみたいです。)


PS: 役に立つ場合は、python に固有の同様の質問を次に示します

そして、同様の質問に対するこの回答には、任意のスクリプトを手早く悪者扱いするための便利なイディオムのように見えるものがあります。

4

13 に答える 13

99

nohupと&演算子を使用して、Unixの実行可能ファイルをデーモン化できます。

nohup yourScript.sh script args&

nohupコマンドを使用すると、スクリプトを強制終了せずにシェルセッションをシャットダウンできます。一方、&はスクリプトをバックグラウンドに配置するため、セッションを続行するためのシェルプロンプトが表示されます。これに関する唯一の小さな問題は、標準出力と標準エラーの両方が./nohup.outに送信されるため、このマナーで複数のスクリプトを開始すると、それらの出力が絡み合うことになります。より良いコマンドは次のとおりです。

nohup yourScript.sh script args >script.out 2>script.error&

これにより、標準が選択したファイルに送信され、標準エラーが選択した別のファイルに送信されます。標準出力と標準エラーの両方に1つのファイルだけを使用する場合は、次のように使用できます。

nohup yourScript.sh script args >script.out 2>&1 &

2>&1は、標準エラー(ファイル記述子2)を標準出力(ファイル記述子1)と同じファイルにリダイレクトするようにシェルに指示します。

コマンドを1回だけ実行し、コマンドが停止した場合に再起動するには、次のスクリプトを使用できます。

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

最初の引数は、使用するpidファイルの名前です。2番目の引数はコマンドです。そして、他のすべての引数はコマンドの引数です。

このスクリプトにrestart.shという名前を付けると、次のように呼ばれます。

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &
于 2010-03-11T08:44:53.467 に答える
34

長い回答で申し訳ありません(私の回答が仕様にどのように適合するかについてのコメントを参照してください)。私は包括的であるように努めているので、あなたは可能な限り足を踏み入れることができます. :-)

プログラムをインストールでき (root アクセス権を持っている)、スクリプトをデーモン実行用に設定するための 1 回限りの作業を喜んで行う場合 (つまり、コマンド ラインで実行するコマンド ライン引数を単に指定するよりも複雑です。サービスごとに 1 回だけ実行する必要があります)、より堅牢な方法があります。

これには、daemontoolsの使用が含まれます。この投稿の残りの部分では、daemontools を使用してサービスをセットアップする方法について説明します。

初期設定

  1. デーモンツールのインストール方法の指示に従ってください。一部のディストリビューション (Debian、Ubuntu など) には既にパッケージが含まれているため、それを使用してください。
  2. というディレクトリを作成し/serviceます。インストーラーはすでにこれを行っているはずですが、確認するか、手動でインストールする場合。この場所が気に入らない場合は、svscanbootスクリプトで変更できますが、daemontools のほとんどのユーザーは使用に慣れており、使用/serviceしないと混乱します。
  3. Ubuntu や、標準を使用しないinit(つまり を使用しない) 別のディストリビューションを使用している場合は、 によって呼び出されるように配置するためのベースとして/etc/inittab、プリインストールされた を使用する必要があります。難しいことではありませんが、OS が使用する を構成する方法を知っておく必要があります。サービスを検索する主な作業を行うを呼び出すスクリプトです。から呼び出されるため、何らかの理由で停止した場合に再起動するように手配します。inittabsvscanbootinitinitsvscanbootsvscaninitinit

サービスごとの設定

  1. 各サービスには、サービスに関するハウスキーピング情報を格納するサービス ディレクトリが必要です。これらのサービス ディレクトリを格納する場所を作成して、それらをすべて 1 か所にまとめることもできます。通常は を使用/var/lib/svscanしますが、新しい場所であれば問題ありません。
  2. 私は通常、スクリプトを使用してサービス ディレクトリを設定し、多くの手動の反復作業を省きます。例えば、

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
    

    ここsome-service-nameで、 はサービスに付ける名前、userはそのサービスを実行するloguserユーザー、 はロガーを実行するユーザーです。(ロギングについては少しだけ説明します。)

  3. サービスはフォアグラウンドで実行する必要があります。プログラムがデフォルトでバックグラウンドになっているが、それを無効にするオプションがある場合は、そうしてください。プログラムを無効にする方法がない場合は、 を読んでくださいfghack。ただし、これにはトレードオフがあります: を使用してプログラムを制御することはできなくなりますsvc
  4. スクリプトを編集して、意図したrunとおりに動作するようにします。sleepサービスが頻繁に終了することが予想される場合は、上部に電話をかける必要がある場合があります。
  5. すべてが正しく設定され/serviceたら、サービス ディレクトリを指すシンボリック リンクを作成します。( サービス ディレクトリを 内に直接配置しないでください。 の監視/serviceからサービスを削除するのが難しくなります。)svscan

ロギング

  1. ロギングのデーモンツールの方法は、サービスにログメッセージを標準出力 (または で生成されたスクリプトを使用している場合は標準エラー) に書き込むことですmkservicesvscanロギング サービスへのログ メッセージの送信を処理します。
  2. ロギング サービスは、標準入力からログ メッセージを受け取ります。によって生成されたログ サービス スクリプトは、mkservice自動ローテーションされ、タイムスタンプが付けられたログ ファイルをlog/mainディレクトリに作成します。現在のログ ファイルの名前はcurrent.
  3. ロギング サービスは、メイン サービスとは別に開始および停止できます。
  4. ログ ファイルをパイプするtai64nlocalと、タイムスタンプが人間が判読できる形式に変換されます。(TAI64N は、ナノ秒カウントの 64 ビットのアトミック タイムスタンプです。)

サービスの制御

  1. svstatサービスのステータスを取得するために使用します。ロギング サービスは独立しており、独自のステータスを持っていることに注意してください。
  2. を使用して、サービス (開始、停止、再起動など) を制御しますsvc。たとえば、サービスを再起動するには、svc -t /service/some-service-name;を使用します。-t「送るSIGTERM」という意味です。
  3. 利用可能なその他の信号には、-h( SIGHUP)、-a( SIGALRM)、-1( SIGUSR1)、-2( SIGUSR2)、および-k( SIGKILL) があります。
  4. サービスを停止するには、 を使用します-d。サービスディレクトリに名前の付いたファイルを作成することで、起動時にサービスが自動的に開始されないようにすることもできますdown
  5. サービスを開始するには、 を使用します-u。これは、以前にダウンさせた (または自動起動しないように設定した) 場合を除き、必要ありません。
  6. スーパーバイザーに退出を求めるには、-x;を使用します。通常-d、サービスを終了するためにも使用されます。これは、サービスを削除できるようにする通常の方法ですが、/service最初にサービスをリンク解除する必要があります。そうしないsvscanと、スーパーバイザが再起動されます。また、ログ サービス ( ) を使用してサービスを作成した場合は、サービス ディレクトリを削除する前にmkservice -lログ スーパーバイザ ( など) も終了することを忘れないでください。svc -dx /var/lib/svscan/some-service-name/log

概要

長所:

  1. daemontools は、サービスを作成および管理する防弾方法を提供します。サーバーに使用していますが、強くお勧めします。
  2. サービスの自動再起動機能と同様に、そのログ システムは非常に堅牢です。
  3. ユーザーが作成/調整するシェル スクリプトでサービスを開始するため、サービスを自由にカスタマイズできます。
  4. 強力なサービス制御ツール: ほとんどすべてのシグナルをサービスに送信でき、サービスを確実に起動および停止できます。
  5. サービスは、クリーンな実行環境が保証されinitます。提供されるものと同じ環境、プロセス制限などで実行されます。

短所:

  1. 各サービスには少しセットアップが必要です。ありがたいことに、これを行う必要があるのはサービスごとに 1 回だけです。
  2. サービスは、フォアグラウンドで実行するように設定する必要があります。また、最良の結果を得るには、syslog やその他のファイルではなく、標準出力/標準エラーにログを記録するように設定する必要があります。
  3. デーモンツールのやり方に慣れていない場合は、学習曲線が急になります。を使用してサービスを再起動する必要がsvcあり、実行スクリプトを直接実行することはできません (スーパーバイザーの制御下にないため)。
  4. 大量のハウスキーピング ファイルと多数のハウスキーピング プロセス。各サービスには独自のサービス ディレクトリが必要であり、各サービスは 1 つのスーパーバイザー プロセスを使用して、サービスが停止した場合にサービスを自動再起動します。(多数のサービスがある場合は、プロセス テーブルに多数superviseプロセスが表示されます。)

総合すると、daemontools はあなたのニーズを満たす優れたシステムだと思います。設定方法やメンテナンス方法についてのご質問をお待ちしております。

于 2010-03-18T04:21:26.757 に答える
14

daemonizeを見てください。2 番目のコピーを検出できます (ただし、ファイル ロック メカニズムを使用します)。また、さまざまな UNIX および Linux ディストリビューションで動作します。

アプリケーションをデーモンとして自動的に起動する必要がある場合は、適切な init-script を作成する必要があります。

次のテンプレートを使用できます。

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0
于 2010-03-17T02:52:10.557 に答える
12

試してみてはいかがでしょうかstart-stop-daemon(8)/etc/init.d例については、Linuxディストリビューションのスクリプトを確認してください。呼び出されたコマンドラインまたはPIDファイルによって開始されたプロセスを見つけることができるため、スクリプトのウォッチドッグであることを除いて、すべての要件に一致します。ただし、必要に応じてスクリプトを再起動するだけの別のデーモンウォッチドッグスクリプトをいつでも開始できます。

于 2009-02-09T01:25:44.127 に答える
7

前述のdaemonizeおよびの代替として、libslack パッケージdaemontoolsdaemonコマンドがあります。

daemon非常に構成可能であり、自動再起動、ログ記録、または pidfile 処理などの面倒なデーモンのすべてを処理します。

于 2012-06-09T14:46:06.790 に答える
5

Daemontools ( http://cr.yp.to/daemontools.html ) は、dj bernstein によって書かれた、これを行うために使用されるかなりハードコアなユーティリティのセットです。私はこれを使用していくつかの成功を収めました。それに関する厄介な部分は、スクリプトを実行しても目に見える結果が返されないことです-目に見えないリターンコードだけです。しかし、一度実行すると防弾です。

于 2010-03-17T05:11:50.143 に答える
5

特に OS X を使用している場合は、launchd の仕組みを確認することをお勧めします。スクリプトが実行されていることを自動的に確認し、必要に応じて再起動します。また、あらゆる種類のスケジューリング機能などが含まれています。要件 1 と 2 の両方を満たす必要があります。

スクリプトのコピーを 1 つだけ実行できるようにするには、PID ファイルを使用する必要があります。通常、現在実行中のインスタンスの PID を含むファイルを /var/run/.pid に書き込みます。プログラムの実行時にファイルが存在する場合、ファイル内の PID が実際に実行されているかどうかを確認します (プログラムがクラッシュしたか、PID ファイルを削除し忘れた可能性があります)。そうである場合は、中止します。そうでない場合は、実行を開始して PID ファイルを上書きします。

于 2009-02-08T08:33:25.983 に答える
3

最初にhttp://code.activestate.com/recipes/278731/createDaemon()から取得します

次にメインコード:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)
于 2009-02-08T09:20:16.433 に答える
1

これは、空のディレクトリにコピーして試すことができる例を含む完全な作業バージョンです ( Getopt::LongFile::SpecFile::Pid、およびIPC::Systemである CPAN 依存関係をインストールした後)。 :Simple -- すべてかなり標準的で、あらゆるハッカーに強く推奨されます: で一度にすべてインストールできますcpan <modulename> <modulename> ...)。


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

例.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

上記の例を次のように呼び出す./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3と、ファイルpidfileが作成され、出力が表示されます。

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3
于 2010-03-18T00:48:13.547 に答える