ホームページ >バックエンド開発 >Python チュートリアル >Python モジュールの先頭で実行されるコードによって引き起こされるバグの詳細な分析
次に、対話型 Python プロンプトでテストしました。
>>> import subprocess >>> subprocess.check_call("false") 0
他のマシンで同じコードを実行すると、次のエラーが正しくスローされました:
>>> subprocess.check_call("false") Traceback (most recent call last): File "", line 1, in File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 542, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command 'false' returned non-zero exit status 1
サブプロセスは子プロセスが正常に終了したと誤って認識したようです。
詳細な分析
一見すると、この問題は Python 自体またはオペレーティング システムによって引き起こされるはずです。そこで、私の同僚はサブプロセスの wait() メソッドを調べました。
def wait(self): """Wait for child process to terminate. Returns returncode attribute.""" while self.returncode is None: try: pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) except OSError as e: if e.errno != errno.ECHILD: raise # This happens if SIGCLD is set to be ignored or waiting # for child processes has otherwise been disabled for our # process. This child is dead, we can't get the status. pid = self.pid sts = 0 # Check the pid and loop as waitpid has been known to return # 0 even without WNOHANG in odd situations. issue14396. if pid == self.pid: self._handle_exitstatus(sts) return self.returncodeos.waitpid の ECHILD 検出が失敗した場合、エラーはスローされないことがわかります。通常、プロセスが終了すると、親プロセスが wait() メソッドを呼び出すまで、システムはその情報を記録し続けます。この期間中、このプロセスは「ゾンビ」と呼ばれます。子プロセスが存在しない場合、成功したか失敗したかを知る方法はありません。
上記のコードは別の問題も解決できます。Python はデフォルトで子プロセスが正常に終了したと想定しますが、プロセスが子プロセスの SIGCHLD を明示的に無視する場合、waitpid() は常に実行します。成功しました。
元のコードに戻ります
プログラムで SIGCHLD を明示的に無視しましたか? 多数の子プロセスを使用しているため、可能性は低いですが、git grep を再度使用した後、同じ問題が発生するのは別の部分だけであることがわかりました。コードでは SIGCHLD を無視しましたが、この世代はプログラムの一部ではなく、単なる参照です。
一週間後
1 週間後、このエラーが再び発生しました。簡単なデバッグにより、デバッガでエラーが再現されました。
いくつかのテストの結果、バグは SIGCHLD を無視するプログラムによって引き起こされたことが判明しました。しかし、なぜこれが起こったのでしょうか?
独立したコードの一部を調べたところ、次のような部分がありました。
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
このコードを誤ってプログラムにインポートしたのでしょうか? このコードがインポートされたとき、上記のステートメントは関数内ではなくこのモジュールの最上位にあったため、結果は「When it was running」になりました。 、SIGCHLD が無視されたため、子プロセス エラーがスローされませんでした
。
このバグの発生により、2 つの教訓が得られました。1 つ目は、新しいコードでエラーが発生する可能性が非常に高いため、デバッグするときは、新しいコードから古いコードに移動し、次に Python ライブラリに移動する必要があるということです。古いコードの場合、Python ライブラリでエラーが発生する可能性はさらに低くなります。
2 番目に、副作用を引き起こす可能性のあるコードはモジュールのトップレベルに書かず、関数内に書きます。モジュールがインポートされるとトップレベルのコードが実行され、さまざまな未知のイベントが発生するためです。 . 起こります。