
Ubuntu 上で次の構成でサービスを実行しています。
#/etc/init/my_service.conf
start on (local-filesystems and net-device-up IFACE=eth1)
respawn
exec python -u /opt/XYZ/my_prog.py 2>&1 \
| logger -t my_prog.py
でサービスを停止してもsudo service my_service stop
、Python プロセスは終了されません。 で親プロセスを終了してkill
も、Python プロセスは終了されません。
サービスを完全に停止するにはどうすればいいでしょうか (つまり、すべての子プロセスを終了させるには)? 理想的には、上記の構成ファイルを変更しないようにしたいと思います。
答え1
理想的には、上記の設定ファイルを変更しないことをお勧めします。
大変だ!それが正しいことだ。
exec
を に変更しscript
、パイプラインの一部としてフォークされたサブプロセスでその Python プログラムの実行を停止する 必要があります。このServerFaultの回答埋め込みシェル スクリプトでこれを行う方法を説明しています。そこに示されているスクリプトの最後の行に 1 つだけ変更を加えます。
exec python -u /opt/XYZ/my_prog.py 2>&1
結局のところ、標準エラーもログに記録しない理由は特にありません。
expect daemon
からへの分岐に対処するためのますます複雑な回転は、systemd
正しいことはデーモンのフォークを停止する現在の騒動から得られる良いことが一つあるとすれば、それは IBM が 1995 年に書いて推奨したことが、これまでずっと正しかったことが継続的に確認されたことだ。
という考え方に慣れるチェーンローディングデーモン。このようなことを簡単にするツールセットはたくさんあります。シェルスクリプトを使わないという考え方にも慣れてください。この作業のために特別に設計されたツールセットはたくさんあり、シェルのオーバーヘッドを排除します(これはUbuntuの世界では良いアイデアとして知られています)。
例えば、ServerFaultの回答にあるシェルコマンドは、次のようなスクリプトに置き換えることができます。ローラン・ベルコのexecline
道具これらは、サブシェルやリンクされていない FIFO なしでまさにこのことを実行できるように設計されています。
#!/command/execlineb -PW
pipeline -w {
logger -t my_prog.py
}
fdmove -c 2 1
python -u /opt/XYZ/my_prog.py
そうすると、
exec /foo/this_execlineb_script
と私のnosh
ツールセット同様に、次の内容を含むスクリプトになります。
#!/usr/local/bin/nosh
pipe
fdmove -c 2 1
python -u /opt/XYZ/my_prog.py | logger -t my_prog.py
あるいは、このスタンザを Upstart ジョブ定義に直接含めることもできます (シェルのメタ文字を回避するトリックを使用して、Upstart がシェルを生成しないようにします)。
exec /usr/local/bin/exec pipe --separator SPLIT fdmove -c 2 1 python -u /opt/XYZ/my_prog.py SPLIT logger -t my_prog.py
参考文献
- ラス・オールベリー(2013-12-18)。 Bug#727708: Debian の upstart 提案ポリシー. debian-ctte.
- ジョナサン・デ・ボイン・ポラード(2001年)。デーモンをバックグラウンドに置くために fork() を実行しないでください。「」。 Unixデーモンプログラムを設計する際に避けるべき間違いよくある質問。
答え2
GNU/Linuxでは、通常、サービスとそれが生成したすべての子プロセスを停止する方法はありません。子プロセスはPPID(親プロセスID)を変更できるためです。唯一の方法は、痕跡システムは、プロセスが作成されるたびにそれを生成し、これらのプロセスのリストを保持します。
Ubuntuのinitシステムではupstart
、これは行われません。したがって、あなたの質問に対する答えはそれは不可能だ-- Ubuntu の場合 -- 以下なし:
- そのスクリプトを変更する。
- そのプロセスによって生成されたプロセス ID を正確に把握する。
- これらのプロセス ID を手動で追跡します。
- 彼らを個別に殺害する。
これが、Linuxディストリビューションを実行する理由です。システムご覧のとおり、systemdすべての子プロセスを追跡する1つのコマンドでそれらすべてを終了できます。これがGNU/Linuxシステム管理の仕組みですすべきであるしかし、systemd は非常に新しいため、また「ここで発明されたものではない」(つまり、Canonical が発明したものではない) ため、Ubuntu はそれを使いたくありません。
答え3
スキップしました期待するスタンザ。新興料理本指定します:
警告
この節は非常に重要です。このセクションを注意深く読んでください。
Upstart は、ジョブに属していると思われるプロセス ID を追跡します。ジョブがインスタンス スタンザを指定している場合、Upstart はそのジョブの一意のインスタンスごとに PID を追跡します。
expect スタンザを指定しない場合、Upstart は exec または script スタンザで実行する最初の PID のライフ サイクルを追跡します。ただし、ほとんどの Unix サービスは「デーモン化」します。つまり、初期プロセスの子である新しいプロセス (fork(2) を使用) を作成します。多くの場合、サービスは「ダブル フォーク」して、初期プロセスとはまったく関係がないようにしています (2 回以上フォークしても追加のメリットがないため、最初はどのサービスも 2 回以上フォークしないことに注意してください)。
この場合、Upstart にはそれを追跡する方法が必要なので、expect fork を使用するか、Upstart が ptrace(2) を使用して「フォークをカウント」できるようにする expect daemon を使用できます。
Upstart がジョブの最終プロセス ID を決定できるようにするには、そのプロセスが fork(2) を呼び出す回数を知る必要があります。デーモンが実行されるや否や、多数の「ワーカー」プロセスをフォークし、それらのプロセス自体が何度でもフォークする可能性があるため、Upstart 自身はこの質問の答えを知ることはできません。この場合、ワーカー プロセスが作成されるかどうか、また何回作成されるか、プロセスが最初に何回フォークするかは不明であるため、Upstart はどの PID が「マスター」であるかを知ることはできません。そのため、どの PID が「マスター」または親 PID であるかを Upstart に伝える必要があります。これは、expect スタンザを使用して実現されます。
使用しているパイプが|子プロセスを作成するため、これが必要になります。本で見つけることができます高度な Linux プログラミング簡単な紹介では、次のように述べられています。
たとえば、このシェルコマンドは、シェルに2つの子プロセスを生成します。1つはlsそして1つは少ない:
$ ls | less
私が知らないのは、これが1つのフォークを意味するのか、それとも2つのフォークを意味するのかということです。そのため、私はラインを変更する実験をします。復活コードに
expect fork
respawn
または
expect daemon
respawn
これが達成できるとは思えないのみとシステム私のロゴは私がファンであることを明らかに示しているにもかかわらずシステム。