如何终止由popen创建并使用communicate()的进程?

1 投票
4 回答
1986 浏览
提问于 2025-04-16 23:14

我有一个程序,P1,需要运行大约24万次,每次用不同的输入。问题是这个P1会卡住,我得手动强制结束它(用kill命令)。我最开始的解决办法是写一个Python脚本来调用P1,传入合适的输入,然后用popencommunicate来获取输出。但是因为communicate会一直等着输出,所以只要它在等响应,我就无法结束这个进程。我是在Windows系统上操作。

我尝试使用multiprocess功能,但它只运行了P1,却没能把输入发送给它。我怀疑在popen中没有使用管道,所以试了一下,但我想我还是无法从P1那里获取输出。

有没有什么好主意?

# This code run XLE and pass the intended input to it automatically
 def startExe(programPath, programArgStr):
 p = subprocess.Popen(programPath,stdout=subprocess.PIPE,stdin=subprocess.PIPE) p.stdin.write(programArgStr)
 p.communicate()[0]
# Need to kill the process if it takes longer than it should here

def main(folder): 
.. 
#loop
programArgStr = "create-parser"+path1+";cd "+ path2+"/s"+ command(counter) +";exit"

startExe(path, programArgStr)
..

如你所见,如果P1能够成功完成任务,它可以通过传入的退出命令自己退出!

4 个回答

2

当你调用 popen 时,可以指定一个管道或文件描述符来接收这个进程的标准输出(也就是它打印出来的内容):

Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

然后你可以监控你传给 popen 的文件或管道,如果没有任何内容被写入,就可以结束这个进程。

关于 popen 参数的更多信息,可以查看 Python 文档

与其使用 p.communicate,不如尝试直接循环读取输出的每一行:

while True:
    line = p.stdout.readline()
    if not line:
        break
    print ">>> " + line.rstrip()
2

如果你不一定要用Python的话,可以考虑使用Cygwin Bash和timeout(1)命令来设置一个命令的时间限制。不过,Cygwin在处理fork()这个功能时速度不太快,而你又需要创建很多进程,这样就会浪费很多时间在创建进程上(我不太清楚Windows原生的Python在这方面是否更好)。

另外,如果你有P1的源代码,为什么不直接修改它,让它在一次运行中完成多次操作呢?这样一来,你就不用处理创建和结束48万个进程的问题了,这样做会大大提高效率,特别是每次操作的工作量不大的时候。

0

我通过修改现有代码,把关键代码放在一个单独的文件里解决了我的问题。为此,我添加了一行代码,把新创建的进程的PID(进程标识符)写入一个文件。

    #//Should come before p.commiunicate
    WriteStatus(str(p.pid) + "***" + str(gmtime().tm_hour) + "***" + str(gmtime().tm_min))        
    p.communicate()[0]

然后,进程监控程序单独运行,每两分钟检查一次文件中列出的进程是否还在运行。如果还在运行,就把它们杀掉,并删除它们的ID。

 def KillProcess(pid):

    subprocess.Popen("TASKKILL /PID "+ str(pid) + " /F /T" , shell=True)
    subprocess.Popen("TASKKILL /im WerFault.exe /F /T" , shell=True)
    print "kill"

    def ReadStatus(filePath):
    print "Checking" + filePath
    try:
            status = open(mainPath+filePath, 'r').readline()
    except:
            print "file removed" + filePath
            return 0
    if len(status) >0:
            info = status.split("***")                
            time = [gmtime().tm_hour, gmtime().tm_min]
            print time

            # Time deifferences
            difHour = time[0]- int(info[1])
            if difHour == 0: # in the same hour
                    difMin =  time[1]- int(info[2])
            else:
                    difMin = 60 - int(info[2]) + time[1]
            if difMin > 2:
                    try:
                            open(mainPath+filePath, 'w').write("")
                            KillProcess(info[0])
                    except:
                            pass
    return 1

  def monitor():
    # Read all the files
    listFiles = os.listdir(mainPath)
    while len(listFiles)>0:
            #GO and check the contents
            for file in listFiles:
                    #Open the file and Calculate if the process should be killed or not
                    pid = ReadStatus(file)
                    #Update listFiles due to remove of file after finishing the process '
                    # of each folder is done
                    listFiles = os.listdir(mainPath)
            for i in range(0,4):
                    time.sleep(30) #waits 30 sec
                    subprocess.Popen("TASKKILL /im WerFault.exe /F /T" , shell=True)
    #to indicate the job is done
    return 1

撰写回答