プログラミング
Pythonのシグナルハンドラ内でprintを呼び出すのは安全か?
Is it safe to call print in a Python signal handler? (iafisher.com)
要約
Pythonのシグナルハンドラ内でprint関数を呼び出すことの安全性について、極端な状況下でのテスト結果が示されています。通常、シグナルハンドラ内でのI/O操作は推奨されませんが、CPythonの実装ではユーザー定義のシグナルハンドラが低レベルのCシグナルハンドラから分離されているため、C言語のような厳しい制約はありません。しかし、シグナルハンドラが再入可能である場合、極端な信号の連続受信によりprint呼び出しがクラッシュする可能性があることが実験で示されました。
全文翻訳
Pythonのシグナルハンドラ内でprintを呼び出すのは安全か?
シリーズ:Pythonにおけるシグナルハンドリング
Pythonには2つのシグナルハンドラがある
シグナルをブロックすることは、無視することとは異なる
PythonはSA_RESTARTを渡さない
安全なシグナルイディオム
Pythonのシグナルハンドラは予期せず再入可能である
Pythonのシグナルハンドラ内でprintを呼び出すのは安全か?
time.sleepとシグナル割り込み
Pythonのsiginterruptは紛らわしい
以前に、Pythonには2つのシグナルハンドラがあるため、シグナルハンドラが呼び出し可能な関数に対する厳格な制限はPythonには適用されないことを学びました。なぜなら、CPythonは、それらの制限が適用される低レベルのCシグナルハンドラ内でユーザー提供のPythonシグナルハンドラを呼び出すのではなく、インタプリタが一貫した状態になったときに後で呼び出されるように手配するからです。また、Pythonのシグナルハンドラは予期せず再入可能であることも学びました。つまり、Pythonシグナルハンドラが実行中にシグナルが到着した場合、最初の呼び出しの途中でシグナルハンドラが再度呼び出される可能性があります。printの呼び出しの途中でシグナルハンドラが再入された場合、どうなるでしょうか?
自分自身に急速な信号の連射を送ることで、それをストレステストしてみましょう。
import os, signal, subprocess
def sighandler(_signo, _frame):
print("signal received")
signal.signal(signal.SIGUSR1, sighandler)
subprocess.run("for x in {1..50}; do kill -USR1 %s; done" % os.getpid(), shell=True)
このプログラムを私のマシンで実行したところ、以下のようになりました。
File "multiple_signals.py", line 6, in sighandler
print("signal received")
File "multiple_signals.py", line 6, in sighandler
print("signal received")
File "multiple_signals.py", line 6, in sighandler
print("signal received")
[Previous line repeated 2 more times]
RuntimeError: reentrant call inside <_io.BufferedWriter name='<stdout>'>
このテストプログラムは、極端な状況下では、シグナルハンドラ内でprintを呼び出すとプログラムがクラッシュする可能性があることを示しています。この状況は極端な状況を必要とすることを強調したいと思います。実際のプログラムがこれらの条件に直面する可能性は低いですが、それでも例外で失敗することは、C言語における安全でないシグナルハンドラの潜在的な結果(デッドロック、データ構造の破損、サイレントフェイルなど)よりも許容できます。したがって、私はこれをシグナルに関する別のトリビアとして扱い、シグナルハンドラを書く上での実用的な考慮事項ではないと考えていますが、それでもシグナルハンドラで些細でない作業を行うことは避けるべきだとアドバイスします。