Python无法使用process.terminate终止进程

2 投票
1 回答
3866 浏览
提问于 2025-04-18 03:48

我有一段Python代码,如下所示:

import threading
import time
import subprocess, os, sys, psutil, signal
from signal import SIGKILL

def processing():
    global p_2
    global subp_2
    .
    .
    .
    if condition1: #loop again
          threading.Timer(10,processing).start()
    if condition2:
          signal.signal(signal.SIGINT, signal_handler)
          #signal.signal(signal.SIGTERM, signal_handler)
          subp_2.terminate()   
          #os.kill(subp_2.pid, 0)
          #subp_2.kill()            
          print " Status p_2: ", p_2.status

def signal_handler(signal, frame):
    print('Exiting')
    sys.exit(0)

def function():
    global p_1
    global subp_1
    .
    .
    .
    if condition1: #loop again
          threading.Timer(5,function).start()

    if condition2:

          signal.signal(signal.SIGINT, signal_handler)
          #signal.signal(signal.SIGTERM, signal_handler)
          subp_1.terminate()   
          #os.kill(subp_1.pid, 0)
          #subp_1.kill()            
          print " Status p_1: ", p_1.status
          threading.Timer(10,processing).start()  
          subp_2 =  subprocess.Popen('./myScript2.sh %s %s' % (arg0, arg1), shell=True)
          p_2 = psutil.Process(subp_2.pid)

if __name__ == '__main__':
          global p_1
          global subp_1 
          .
          .
          .
          subp_1 = subprocess.Popen(["/.../myScript1.sh"],  shell=True)
          p_1 = psutil.Process(subp_1.pid)
          threading.Timer(5,function).start()

我无法结束进程subp_1和subp_2。不管我尝试什么方法:.terminate()、.kill()或者os.kill(),它们的状态仍然显示为正在运行。有人能告诉我我漏掉了什么吗?任何提示都很感谢。

1 个回答

4

当你使用 shell=True 时,首先会启动一个子进程来运行命令行(shell)。然后,这个命令行又会启动一个子进程来运行 myScript2.sh。运行命令行的子进程可以被结束,而不会影响到 myScript2.sh 的子进程。

如果能避免使用 shell=True,那就可以避免这个问题。如果你是用用户输入来组成命令,绝对应该避免使用 shell=True,因为这会带来 安全风险

在Unix系统中,默认情况下,通过 subprocess.Popen 启动的子进程并不是一个 会话领导者。当你向一个会话领导者发送信号时,这个信号会传递给所有具有相同会话ID的进程。因此,要让命令行将 SIGTERM 信号传递给 myScript2.sh,需要让命令行成为会话领导者。

对于Unix系统上Python版本低于3.2的情况,可以通过让命令行进程运行 os.setsid() 来实现:

import os
subp_2 =  subprocess.Popen('./myScript2.sh %s %s' % (arg0, arg1), 
                           shell=True, 
                           preexec_fn=os.setsid)

# To send SIGTERM to the process group:
os.killpg(subp_2.pid, signal.SIGTERM)

对于Unix系统上Python版本3.2及以上的情况,可以在调用 Popen 时传入 start_new_session=True

在Windows系统上,可以参考 J.F. Sebastian的解决方案

撰写回答