Python プロセスが再起動後に SIGTERM / SIGINT に応答しなくなる
質問
ウォッチドッグ プロセスを使用して実行されている一部の Python プロセスで奇妙な問題が発生しています。
ウォッチドッグ プロセスは Python で書かれており、親プロセスであり、次の関数があります。 開始子(名前) を使用する サブプロセス.Popen 子プロセスを開きます。Popen オブジェクトは、ウォッチドッグが次を使用してプロセスを監視できるように記録されます。 ポーリング() そして最終的にはこれで終わります 終了() 必要なときに。子どもが予期せず死亡した場合、番犬が電話をかけます。 開始子(名前) 再度、新しい Popen オブジェクトを記録します。
子プロセスは 7 つあり、それらもすべて Python です。いずれかの子を手動で実行する場合は、次を使用して SIGTERM または SIGINT を送信できます。 殺す 期待した結果が得られます (プロセスは終了します)。
ただし、ウォッチドッグ プロセスから実行される場合、子プロセスは終了後にのみ終了します。 初め 信号。ウォッチドッグが子プロセスを再起動すると、新しい子プロセスは SIGTERM または SIGINT に応答しなくなります。何が原因なのか分かりません。
ウォッチドッグ.py
class watchdog:
# <snip> various init stuff
def start(self):
self.running = true
kids = ['app1', 'app2', 'app3', 'app4', 'app5', 'app6', 'app7']
self.processes = {}
for kid in kids:
self.start_child(kid)
self.thread = threading.Thread(target=self._monitor)
self.thread.start()
while self.running:
time.sleep(10)
def start_child(self, name):
try:
proc = subprocess.Popen(name)
self.processes[name] = proc
except:
print "oh no"
else:
print "started child ok"
def _monitor(self):
while self.running:
time.sleep(1)
if self.running:
for kid, proc in self.processes.iteritems():
if proc.poll() is not None: # process ended
self.start_child(kid)
それで何が起こるかというと、 ウォッチドッグ.スタート() 7 つのプロセスすべてを起動します。プロセス SIGTERM を送信すると、そのプロセスは終了し、モニター スレッドが再びプロセスを開始します。ただし、その後新しいプロセス SIGTERM を送信すると、それは無視されます。
再起動されたプロセスに kill -15 を何度も送信し続けることができるはずです。再起動後に無視するのはなぜですか?
解決
ここで説明されているように: http://blogs.gentoo.org/agaffney/2005/03/18/python_sucks 、Python が新しいスレッドを作成すると、そのスレッド (およびスレッドが生成するプロセス) のすべてのシグナルがブロックされます。
ctypes を通じて呼び出される sigprocmask を使用してこれを修正しました。これは「正しい」方法である場合もあれば、そうでない場合もありますが、機能します。
子プロセスでは、 __init__
:
libc = ctypes.cdll.LoadLibrary("libc.so")
mask = '\x00' * 17 # 16 byte empty mask + null terminator
libc.sigprocmask(3, mask, None) # '3' on FreeBSD is the value for SIG_SETMASK
他のヒント
ctypes を使用するのではなく、Python 内でデフォルトのシグナル ハンドラーを復元した方が良いのではないでしょうか?子プロセスで、シグナル モジュールを使用します。
import signal
for sig in range(1, signal.NSIG):
try:
signal.signal(sig, signal.SIG_DFL)
except RuntimeError:
pass
SIGKILL などのキャッチできないシグナルを設定しようとすると、RuntimeError が発生します。