web-dev-qa-db-ja.com

シェルスクリプトデーモンを作成するときに、どのPIDがsystemd PIDFileセクションに属していますか?

this answer のコードに基づいて、シェルスクリプトデーモンを作成しました。次の内容でsystemdサービスファイルを作成しました。

[Unit]
Description=My Daemon
After=network.target

[Service]
Type=forking
PIDFile=/run/daemon.pid
ExecStart=/root/bin/daemon.sh
ExecReload=/bin/kill -1 -- $MAINPID
ExecStop=/bin/kill -- $MAINPID
TimeoutStopSec=5
KillMode=process

[Install]
WantedBy=multi-user.target

Whileループが始まる直前に、PIDファイルを作成しています(この時点では、子または親ではなく、デーモンによって作成されます)。echo $$ > /run/daemon.pid; 。正常に動作しますが、systemctl status daemon.serviceを呼び出すたびに、次の警告が表示されます。

daemon.service: PID file /run/daemon.pid not readable (yet?) after start: No such file or directory

PID creatonステートメントecho $$ > /run/daemon.pid;をスクリプトの最初に挿入すると(子と親でも使用されます)、次の警告が表示されます。

daemon.service: PID 30631 read from file /run/daemon.pid does not exist or is a zombie.

Systemdで警告メッセージを表示せずにPIDファイルを作成するための最良の方法は何ですか?

7
manifestor

したがって、ここで見られる問題は、Type=forkingが使用されている場合、親プロセスが終了する前にpidファイルを(正しいpidで)作成する必要があるためです。

子からpidfileを作成すると、親の終了と競合し、場合によっては(多くの場合)最初に表示されるエラーが発生します。

子を開始する前に$$を書き込むpidfileを作成すると、終了した親のpidが含まれるため、他のエラーが表示されます。

これを正しく行う1つの方法は、終了する直前に、親からpidfileを書き込むことです。その場合、$!$$ではなく)を書き込みます。これは、バックグラウンドで生成された最後のプロセスのPIDを返します。

例えば:

#!/bin/bash

# Run the following code in background:
(
    while keep_running; do
        do_something
    done
) &

# Write pid of the child to the pidfile:
echo "$!" >/run/daemon.pid
exit

これは正しく動作するはずです... [〜#〜]ただし[[##〜]、これを達成するためのより良い方法があります!読む...


実際、systemdの全体のポイントは、プロセスをデーモン化してバックグラウンドで実行することです...自分でそれを行おうとすると、systemdがそれを実行できないようになります。それは同時にあなたの人生をはるかに難しくしています...

Type=forkingを使用する代わりに、シェルスクリプトを記述してをフォアグラウンドで実行し、Type=simpleを使用するようにサービスを設定します。その場合、pidfileは必要ありません。

これを行うために/root/bin/daemon.shを更新します。

#!/bin/bash

# Run the following code in foreground:
while keep_running; do
    do_something
done

(注:おそらくdaemon.shは現時点では最適な名前ではありません...それはバックグラウンドで実行されることを意味するためです。実際の動作に関連して、より適切な名前を付けてください。)

次に、.serviceファイルを更新してType=simpleを使用します(実際には、ここではデフォルトで使用されるため、省略することもできます)。

[Service]
Type=simple
ExecStart=/root/bin/daemon.sh
ExecReload=/bin/kill -1 -- $MAINPID
ExecStop=/bin/kill -- $MAINPID
TimeoutStopSec=5
KillMode=process

ところで、シグナルでプロセスを強制終了することもデフォルトの動作なので、おそらくExecStop=をドロップできます...

systemdのType=forkingは、実際にはそのようにしか機能せず、フォアグラウンドで機能するように簡単に修正できないレガシープログラムにのみ存在します...ハックで非効率的です。 systemd(およびその代わりとなるもののいくつか)の全体的なポイントは、フォークとデーモン化自体を行い、サービスに必要なことだけを行わせることです! :-)

これがお役に立てば幸いです...そして、systemdに重い作業を任せてくれることを本当に期待しています!その方がはるかに効率的です。

11
filbranden

次のステートメントをサービスファイルに追加すると、エラーメッセージdaemon.service: PID file /run/daemon.pid not readable (yet?) after start: No such file or directoryが消えました。

ExecStartPost=/bin/sleep 2
4
manifestor