【问题标题】:Kill background process launched with shell=True in Windows在 Windows 中终止使用 shell=True 启动的后台进程
【发布时间】:2019-03-08 21:49:28
【问题描述】:

我正在尝试从 Python 启动 server to score PMML models in Java。计算完成后,我希望能够关闭该服务器(即终止进程)。我使用的是 Windows 10。但是,我发现它比我想象的更令人困惑。

首先我像这样启动服务器:

p = subprocess.Popen('START /B java -jar openscoring-server-executable-1.4.3.jar',
                      shell=True)

我让它在后台运行,这样当用户运行脚本时,CMD 窗口就不会出现。为了使这成为可能,我认为shell=True 参数是必要的。

当我尝试使用此行终止进程时:

subprocess.call("TASKKILL /F /PID {pid} /T".format(pid=p.pid))

进程仍在运行。事实上,p.pid 返回的 PID 甚至在任务管理器中都不存在。我在尝试使用this answer 中提供的解决方案时确认了这一点:

def kill(proc_pid):
    process = psutil.Process(proc_pid)
    for proc in process.children(recursive=True):
        proc.kill()
    process.kill()

kill(p.pid)

这会返回异常NoSuchProcess: no process found with pid 5280

我可以通过任务管理器查看分配给我要杀死的进程的 PID:

但我不知道如何在 Python 中找到该 PID 来终止它。我在网站上找到的答案似乎不适用于后台运行的进程。

编辑:

如果我能够使用shell=False 运行命令,我认为这可以解决,但由于START /B,我不知道该怎么做。这会返回一个错误:

p = subprocess.Popen(['START', '/B', 'java', '-jar openscoring-server-executable-1.4.3.jar'])

【问题讨论】:

  • 你试过没有START /B吗?
  • @CristiFati 是的,它会打开一个 CMD 窗口,我想避免这种情况。这就是我在命令中引入它的原因。我相信这与我得到的 PID 是对应于创建的 shell 而不是从它启动的进程这一事实有关。我认为这可以通过使用shell=False 来解决,但我不知道使用START /B 时是否可以。
  • 嗯,我创建了一个虚拟 Java 程序,它有一个无限循环,可以打印一些内容并休眠一秒钟,正常运行(使用subprocess.Popen),但它没有打开新窗口,而是将其输出重定向到我启动 Python 进程(其父进程)的控制台中。添加START /B 时的行为是相同的(并且它也与其父对象分离)。
  • 您正在获取START 命令的PID。也许尝试p = subprocess.Popen(['javaw', '-jar openscoring-server-executable-1.4.3.jar'], creationflags=(subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS), close_fds=True)javaw 开头使java 不会产生控制台。其余的让进程独立运行(理论上——我是从谷歌搜索得到的)。
  • @StevenRumbalski, CREATE_NEW_PROCESS_GROUP 对于 javaw.exe 之类的 GUI 进程通常毫无意义。唯一使用进程组的 WinAPI 是 GenerateConsoleCtrlEvent,因此程序必须调用 AllocConsoleAttachConsole 才能使进程组相关。 DETACHED_PROCESS 对于 GUI 应用程序也毫无意义。其目的是在不分配新控制台的情况下将子控制台与当前控制台分离。

标签: python windows cmd subprocess kill-process


【解决方案1】:

此行不起作用,因为您需要提供所有参数和确切的字符串。

if process.cmdline() == args:

我将其更改为包含一些字符串和部分字符串:

def process_get(*args):
        """Returns the process matching ``args``.
        Relies on ``args`` matching the argument list of the process to find.

        Args:
            *args: process arguments, e.g. ["java", "jar", "partialjarname"]

        Returns:
            :obj:`process`
        """
        import psutil
        for process in psutil.process_iter():
            try:
                if all(x in " ".join(process.cmdline()) for x in args):
                    return process
            except psutil.AccessDenied:
                pass
        return None

【讨论】:

    【解决方案2】:

    您可以在这里搜索您的应用并关闭。

    import psutil
    import os
    
    ls = []
    for p in psutil.process_iter():
        try:
            name = p.name()
            cmdline = p.cmdline()
            exe = p.exe()
            pid = p.pid
        except (psutil.AccessDenied, psutil.ZombieProcess):
            continue
        except psutil.NoSuchProcess:
            continue
    

    【讨论】:

      【解决方案3】:

      如果您无法确定正确的 PID,我可能有一个替代解决方案,可以利用您用于启动进程的参数。这不是很好,因为您将遍历流程树直到找到命中,但是这样它肯定会找到流程。

      请记住,如果您有多个具有相同参数的进程正在运行,这可能并不总是返回正确的进程。

      def process_get(*args):
          """Returns the process matching ``args``.
          Relies on ``args`` matching the argument list of the process to find.
      
          Args:
              *args: process arguments, e.g. ["java", "-jar", "somejar.jar"]
      
          Returns:
              :obj:`process`
          """
          import psutil
          for process in psutil.process_iter():
              try:
                  if process.cmdline() == args:
                      return process
              except psutil.AccessDenied:
                  pass
          return None
      

      【讨论】:

        猜你喜欢
        • 2023-03-20
        • 1970-01-01
        • 2016-08-01
        • 2016-08-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多