Pythonのsubprocessでkillが効かないときの対処法


はじめに

Pythonのsubprocess.Popenを使って,サブプロセスを起動・終了させようとしたが終了だけうまくいかなかった.
原因と対処法をメモしておく.

結論

この現象は,subprocess.Popenshell=Trueを指定していた場合に発生する.

対処法は,execコマンドを先頭にくっつけること.

subprocess.Popen('exec 実行するコマンド', shell=True)

再現手順

以下のように,起動する側(run.py)起動される側(wait.py)の2つのスクリプトを書くと簡単に再現できた.

run.py
from subprocess import Popen
import time

p = Popen('python3 wait.py', shell=True)
time.sleep(5)
p.kill()
wait.py
import time

for i in range(1000):
    print(i)
    time.sleep(1)

python run.pyを実行すると,run.pyの終了後もwait.pyのプロセスが存在したままになった.
一方で,Popenによって生成されたシェルのプロセスは正常に終了していた.

原因

subprocess.Popenshell=Trueを指定していたため,コマンドはPopenによって起動されたシェルの中で実行されていた.
killコマンドによってシェルは正常に終了されるが,シェルによって起動されたコマンド(プロセス)は終了できていなかった.

対処法

上が原因であるとすれば,生成されたシェルと実行するコマンドが同じプロセスになればよい.
execコマンドを使うことで,生成されたシェルのプロセスの中でコマンドを実行させるようにする.

これで,.kill()を実行すれば生成されたシェルとともに実行したコマンドも停止できるようになる(なった).

対策後のコードはこんな感じ.

run2.py
from subprocess import Popen
import time

p = Popen('exec python3 wait.py', shell=True)
time.sleep(5)
p.kill()

さいごに

ここで紹介した方法以外にもプロセスグループを作るといった方法もあるらしい.(まだよくわかってない)
生成するプロセスの親子関係や,数が多い場合は考えるべきかもしれない.