为什么终止此子进程会引发 ProcessLookupError?

Why does killing this subprocess raise a ProcessLookupError?

我无法理解何时需要终止子进程。

for package in server.packages:
    n = subprocess.Popen(['which', package], stdout=subprocess.DEVNULL)
    n.wait()
    if n.returncode != 0:
        n.kill()
        <some other code>

我遇到错误(使用 Python3):

ProcessLookupError: [Errno 3] No such process

任何人都可以解释我什么时候子进程自杀以及什么时候我需要手动完成吗?

你可以做的是设置进程的超时时间,一旦超时,你可以使用以下方式终止进程:

n.terminate()

你也可以使用

.is_alive() 

检查进程或线程是否存活,如果超时则终止。

在Python中,Popen.wait是等待子进程终止的阻塞调用。所以通常不需要在调用 wait returns 之后杀死子进程。请参阅 Popen.wait() 上的 Python 文档。

现在,如果您了解它是如何工作的,您会发现您的代码失败了,因为在某个时刻 Popen.returncode returns 一个非零值,然后您尝试终止一个执行此操作的进程不存在了。

这就是引发 ProcessLookupError 的原因。

现在,正如这里的另一个答案所指出的,返回值可能是 None,这表明子进程可能存在(可能是 OS 特定的)问题,并且可能是检查。 Python 文档 merely state None 表示进程仍在 运行ning(负值也是可能的;有关详细信息,请参阅文档)。

除此之外,如果您出于某种原因需要终止静止的 运行ning 子进程,您要么必须使用以下方法设置并捕获超时:

   try:
      # time out in five seconds
      n.wait(timeout=5)
   except TimeOutExpired:
      # you can use kill, terminate or send_signal method; see the docs
      n.kill()

... 或者,根本不使用 wait:相反,当您在代码中执行其他操作时让子进程 运行,然后稍后杀死它们:

   import os

   processes = []
   for package in server.packages:
      n = subprocess.Popen(['which', package], stdout=subprocess.DEVNULL)
      processes.append(n)

   <some other code while the subprocesses run (and possibly terminate)>

   for p in processes:
      try:
          p.kill()
      except OSError:
          # silently fail if the subprocess has exited already
          pass

如果只想检查进程是否存活怎么办?不幸的是,Python stdlib 没有一个好的、方便的方法来检查进程是否 运行ning。但是,可以使用第三方库 psutil 方便地完成。有了它,检查进程是否存在就像:

   pid = n.pid # where n is a subprocess.Popen object from above examples
   import psutil
   psutil.pid_exists(pid) # returns True or False

您必须检查 wait 的结果以确定它返回的原因。如果它 returns None 则由于子进程已终止以外的其他原因返回等待。如果它 returns 不是 None 则它不适合 kill 该过程,但这并不意味着它自动适合 kill 如果它返回 None

例如,根据 OS,None 的返回可能表示您不想终止进程的状态更改。但也可能存在 returns None 但仍然 运行 的情况,可能会在您到达 kill.

之前终止

因此,如果您选择 kill 进程,您可能应该将其包含在 try-catch 语句中,以处理子进程终止的情况。

也可以从 python3.3 添加 timeout(例如 n.wait(timeout=seconds_to_wait) 等待参数,以免无限期等待。

您的代码可能类似于:

for package in server.packages:
    n = subprocess.Popen(['which', package], stdout=subprocess.DEVNULL)
    rc = n.wait() # in python3 you could supply timeout argument
    if rc != None:
        try:
            n.kill()
        except ProcessLookupError:
            pass
        code_to_run_when_killed()
    elif rc != 0:
        code_to_run_when_failed() # or perhaps you should raise exception
    else:
        code_to_run_when_exited_successfully()