【问题标题】:How to check if there exists a process with a given pid in Python?如何检查Python中是否存在具有给定pid的进程?
【发布时间】:2010-10-08 18:29:50
【问题描述】:

有没有办法检查 pid 是否对应于有效进程?我从 os.getpid() 以外的其他来源获取 pid,我需要检查机器上是否不存在具有该 pid 的进程。

我需要它在 Unix 和 Windows 中可用。我也在检查 PID 是否在使用中。

【问题讨论】:

  • Windows 是非标准操作系统。这些东西是不可移植的。知道你不能两者兼得,你的首要任务是什么?选择一个作为优先事项并编辑问题。
  • @S.Lott Windows 是一个非标准操作系统 这是我在 SO...上看到的最愚蠢的评论之一...
  • @Piotr Dobrogost:您能否提供处理 POSIX 标准 unix 和非 POSIX 标准 Windows 的代码?如果是这样,请提供 (a) 解决问题和 (b) 明确说明 Windows 在某种程度上符合 POSIX 标准的答案。
  • @PiotrDobrogost 我认为 S.Lott 的评论更多是关于实施细节和 API 支持,而不是市场份额。
  • Windows 与其他流行操作系统的共同点当然比其他操作系统之间的共同点要少。 (任何从事 Web 开发的人都可能将其比作同样臭名昭著的 Microsoft 产品。)但作为对@S.Lott 的回应:我很少为 Windows 编写不应在 Linux、OSX、BSD 等上运行的 Python 代码,所以我老实说,不要认为“优先选择”是有用的建议,尤其是因为 Python 尽可能地抽象出平台差异。

标签: python process pid


【解决方案1】:

如果 pid 没有运行,向 pid 发送信号 0 将引发 OSError 异常,否则不执行任何操作。

import os

def check_pid(pid):        
    """ Check For the existence of a unix pid. """
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

【讨论】:

  • 在 linux 和 OSX 中肯定可以工作,我不能为 Windows 说话。它不会按照您的要求终止进程,它会发送信号 0,基本上是“您正在运行吗?”。
  • 这在 Windows 上肯定行不通,因为没有类似 UNIX 的信号。
  • 要完整,您还应该检查错误号以确保它是 3(捕获异常并检查第一个 arg)。如果目标进程存在但您无权发送信号(无论出于何种原因),这一点很重要。
  • os.kill 在 Windows 上受支持,但 os.kill(pid, 0)os.kill(pid, signal.CTRL_C_EVENT) 相同,可能会终止进程(或失败)。当我在子进程上尝试这个时,我得到一个OSError where errno==EINVAL
【解决方案2】:

看看psutil 模块:

psutil(python 系统和进程实用程序)是一个跨平台库,用于检索有关正在运行的进程系统利用率(CPU、内存、磁盘、网络)在 Python 中。 [...] 它目前支持 LinuxWindowsOSXFreeBSDSun Solaris,32-bit64-bit 架构,Python 版本从 2.6 到 3.4(Python 2.4 和 2.5 的用户可以使用 2.1.3 版本)。 PyPy 也可以工作。

它有一个名为 pid_exists() 的函数,您可以使用它来检查具有给定 pid 的进程是否存在。

这是一个例子:

import psutil
pid = 12345
if psutil.pid_exists(pid):
    print("a process with pid %d exists" % pid)
else:
    print("a process with pid %d does not exist" % pid)

供参考:

【讨论】:

【解决方案3】:

mluebke 代码不是 100% 正确的; kill() 还可以引发 EPERM(拒绝访问),在这种情况下,这显然意味着存在进程。这应该有效:

(根据 Jason R. Coombs cmets 编辑)

import errno
import os

def pid_exists(pid):
    """Check whether pid exists in the current process table.
    UNIX only.
    """
    if pid < 0:
        return False
    if pid == 0:
        # According to "man 2 kill" PID 0 refers to every process
        # in the process group of the calling process.
        # On certain systems 0 is a valid PID but we have no way
        # to know that in a portable fashion.
        raise ValueError('invalid PID 0')
    try:
        os.kill(pid, 0)
    except OSError as err:
        if err.errno == errno.ESRCH:
            # ESRCH == No such process
            return False
        elif err.errno == errno.EPERM:
            # EPERM clearly means there's a process to deny access to
            return True
        else:
            # According to "man 2 kill" possible error values are
            # (EINVAL, EPERM, ESRCH)
            raise
    else:
        return True

除非您使用 pywin32、ctypes 或 C 扩展模块,否则您无法在 Windows 上执行此操作。 如果你可以依赖外部库,你可以使用psutil:

>>> import psutil
>>> psutil.pid_exists(2353)
True

【讨论】:

  • haridsv 建议测试应该是 e.errno != 3;也许 e.errno != errno.ESRCH
  • 为什么? ESRCH 的意思是“没有这样的过程”。
  • 对。由于 ESRCH 的意思是“没有这样的进程”,所以 errno != ESRCH 的意思是“没有这样的进程”或者“进程存在”,和函数的名字很像。您特别提到了 EPERM,但其他​​可能的错误代码意味着什么?挑出与检查意图松散相关的错误代码似乎是不正确的,而 ESRCH 似乎密切相关。
  • 你是对的。我编辑了现在反映您建议的代码。
  • 在 python3 os.kill() 中抛出 ProcessLookupError
【解决方案4】:

涉及向进程发送“信号 0”的答案只有在相关进程归运行测试的用户所有时才有效。否则,由于权限,您将获得OSError,即使系统中存在该pid。

为了绕过这个限制,你可以检查/proc/&lt;pid&gt;是否存在:

import os

def is_running(pid):
    if os.path.isdir('/proc/{}'.format(pid)):
        return True
    return False

显然,这仅适用于基于 linux 的系统。

【讨论】:

  • 错误。 PermissionError means pid exists,如果 pid 不存在,你会得到 ProcessLookupError
  • OSError 由于被拒绝的权限可以与其他的区别 - 通过查看 errno 或通过捕获来自OSError 的更专业的PermissionError/ProcessLookupError 异常。此外,如果进程存在,您只会收到权限错误。因此,您的示例只是适用于 Linux 和其他一些 Unices 的替代方法,但它并不比正确调用 os.kill(pid, 0) 更完整。
  • 这个解决方案不是跨平台的。 OP 希望它可以在 Unix 和 Windows 上使用。 /proc procfs 仅在 Linux 上存在,甚至在 BSD 或 OSX 上也不存在。
  • 如果 /proc 挂载 hidepid=2,此方法将失败,如果该进程由其他用户拥有,则不会显示在列表中。
【解决方案5】:

在 Python 3.3+ 中,您可以使用异常名称而不是 errno 常量。 Posix version:

import os

def pid_exists(pid): 
    if pid < 0: return False #NOTE: pid == 0 returns True
    try:
        os.kill(pid, 0) 
    except ProcessLookupError: # errno.ESRCH
        return False # No such process
    except PermissionError: # errno.EPERM
        return True # Operation not permitted (i.e., process exists)
    else:
        return True # no error, we can send a signal to the process

【讨论】:

    【解决方案6】:

    查看here 以获取特定于 Windows 的获取运行进程及其 ID 的完整列表的方法。会是这样的

    from win32com.client import GetObject
    def get_proclist():
        WMI = GetObject('winmgmts:')
        processes = WMI.InstancesOf('Win32_Process')
        return [process.Properties_('ProcessID').Value for process in processes]
    

    然后您可以根据此列表验证您获得的 pid。我不知道性能成本,所以如果你要经常做 pid 验证,最好检查一下。

    对于 *NIx,只需使用 mluebke 的解决方案。

    【讨论】:

    • 这对我来说效果很好。我想检查进程名称,因此将“ProcessID”换成“Name”,并将其转换为检查列表以返回 True 或 False。
    【解决方案7】:

    在 ntrrgc 的基础上,我增强了 Windows 版本,因此它会检查进程退出代码并检查权限:

    def pid_exists(pid):
        """Check whether pid exists in the current process table."""
        if os.name == 'posix':
            import errno
            if pid < 0:
                return False
            try:
                os.kill(pid, 0)
            except OSError as e:
                return e.errno == errno.EPERM
            else:
                return True
        else:
            import ctypes
            kernel32 = ctypes.windll.kernel32
            HANDLE = ctypes.c_void_p
            DWORD = ctypes.c_ulong
            LPDWORD = ctypes.POINTER(DWORD)
            class ExitCodeProcess(ctypes.Structure):
                _fields_ = [ ('hProcess', HANDLE),
                    ('lpExitCode', LPDWORD)]
    
            SYNCHRONIZE = 0x100000
            process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
            if not process:
                return False
    
            ec = ExitCodeProcess()
            out = kernel32.GetExitCodeProcess(process, ctypes.byref(ec))
            if not out:
                err = kernel32.GetLastError()
                if kernel32.GetLastError() == 5:
                    # Access is denied.
                    logging.warning("Access is denied to get pid info.")
                kernel32.CloseHandle(process)
                return False
            elif bool(ec.lpExitCode):
                # print ec.lpExitCode.contents
                # There is an exist code, it quit
                kernel32.CloseHandle(process)
                return False
            # No exit code, it's running.
            kernel32.CloseHandle(process)
            return True
    

    【讨论】:

    • 其实根据msdn.microsoft.com/en-us/library/windows/desktop/…GetExistCodeProcess需要PROCESS_QUERY_INFORMATIONPROCESS_QUERY_LIMITED_INFORMATION的访问权限。
    • 注意代码有误。 GetExitCodeProcess 接收一个句柄和一个指针,在这个示例中它接收一个 ExitCodeProcess 结构作为第二个参数,而它应该只是一个指针。
    • 在 OpenProcess 之后,检查 GetLastError 是个好主意。那里的 ERROR_ACCESS_DENIED 表示该进程存在!下面是一个完整的例子:gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
    【解决方案8】:

    结合Giampaolo Rodolà's answer for POSIXmine for Windows 我得到了这个:

    import os
    if os.name == 'posix':
        def pid_exists(pid):
            """Check whether pid exists in the current process table."""
            import errno
            if pid < 0:
                return False
            try:
                os.kill(pid, 0)
            except OSError as e:
                return e.errno == errno.EPERM
            else:
                return True
    else:
        def pid_exists(pid):
            import ctypes
            kernel32 = ctypes.windll.kernel32
            SYNCHRONIZE = 0x100000
    
            process = kernel32.OpenProcess(SYNCHRONIZE, 0, pid)
            if process != 0:
                kernel32.CloseHandle(process)
                return True
            else:
                return False
    

    【讨论】:

    • Windows 版本不适用于 Windows 8.1。你必须检查GetExitCodeProcess 并确保你甚至可以访问。
    • 仅使用kernel32.OpenProcess 是不够的。正如here 指出的“如果进程最近退出,则该句柄可能仍然存在pid。” 如果kernel32.OpenProcess 返回非零值,我们仍然需要kernel32.GetExitCodeProcess 进一步检查退出代码。
    【解决方案9】:

    在 Windows 中,您可以这样做:

    import ctypes
    PROCESS_QUERY_INFROMATION = 0x1000
    def checkPid(pid):
        processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFROMATION, 0,pid)
        if processHandle == 0:
            return False
        else:
            ctypes.windll.kernel32.CloseHandle(processHandle)
        return True
    

    首先,在这段代码中,您尝试获取给定 pid 的进程句柄。如果句柄有效,则关闭进程的句柄并返回True;否则,您返回 False。 OpenProcess 的文档:https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

    【讨论】:

      【解决方案10】:

      这适用于 Linux,例如,如果您想检查 banshee 是否正在运行...(banshee 是一个音乐播放器)

      import subprocess
      
      def running_process(process):
          "check if process is running. < process > is the name of the process."
      
          proc = subprocess.Popen(["if pgrep " + process + " >/dev/null 2>&1; then echo 'True'; else echo 'False'; fi"], stdout=subprocess.PIPE, shell=True)
      
          (Process_Existance, err) = proc.communicate()
          return Process_Existance
      
      # use the function
      print running_process("banshee")
      

      【讨论】:

      • 这种方法显然不如使用os.kill(pid, 0) 或查看/proc/{pid}。您的代码不会执行一个系统调用,而是派生一个子进程,在该子进程中执行一个 shell,shell 解释您多余的迷你 shell 脚本,shell 派生另一个执行 pgrep 的子进程,最后 pgrep 遍历/proc。您的回答没有回答发布的问题。 OP 要求提供一种给定 PID 的方法。您的方法需要一个进程名称。
      【解决方案11】:

      以下代码适用于 Linux 和 Windows,不依赖于外部模块

      import os
      import subprocess
      import platform
      import re
      
      def pid_alive(pid:int):
          """ Check For whether a pid is alive """
      
      
          system = platform.uname().system
          if re.search('Linux', system, re.IGNORECASE):
              try:
                  os.kill(pid, 0)
              except OSError:
                  return False
              else:
                  return True
          elif re.search('Windows', system, re.IGNORECASE):
              out = subprocess.check_output(["tasklist","/fi",f"PID eq {pid}"]).strip()
              # b'INFO: No tasks are running which match the specified criteria.'
      
              if re.search(b'No tasks', out, re.IGNORECASE):
                  return False
              else:
                  return True
          else:
              raise RuntimeError(f"unsupported system={system}")
      

      如果您需要,它可以轻松增强

      1. 其他平台
      2. 其他语言

      【讨论】:

        【解决方案12】:

        我发现这个解决方案似乎在 windows 和 linux 中都运行良好。我用psutil检查。

        import psutil
        import subprocess
        import os
        p = subprocess.Popen(['python', self.evaluation_script],stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
        
        pid = p.pid
        
        def __check_process_running__(self,p):
            if p is not None:
                poll = p.poll()
                if poll == None:
                    return True
            return False
            
        def __check_PID_running__(self,pid):
            """
                Checks if a pid is still running (UNIX works, windows we'll see)
                Inputs:
                    pid - process id
                returns:
                    True if running, False if not
            """
            if (platform.system() == 'Linux'):
                try:
                    os.kill(pid, 0)
                    if pid<0:               # In case code terminates
                        return False
                except OSError:
                    return False 
                else:
                    return True
            elif (platform.system() == 'Windows'):
                return pid in (p.pid for p in psutil.process_iter())
        

        【讨论】:

          【解决方案13】:

          Windows 的另一个选择是通过 pywin32 包:

          pid in win32process.EnumProcesses()
          

          win32process.EnumProcesses() 返回当前运行进程的 PID。

          【讨论】:

            【解决方案14】:

            我会说将 PID 用于您获取它的任何目的并优雅地处理错误。否则,这是一场经典的比赛(当你检查它的有效时,PID可能是有效的,但稍后会消失)

            【讨论】:

            • 我应该更具体 - 我正在检查无效。因此,我基本上希望能够查看 pid 是否未在使用中。
            • 但是你会怎么处理这个答案呢?在您获得该知识后的那一刻,某些东西可能会使用该 pid。
            • @Damien_The_Unbeliever - 如果在我获得这些知识后有什么东西在使用它,那没关系,我理解你对比赛条件的看法,但我可以向你保证,它不适用于我的情况。
            猜你喜欢
            • 2012-02-27
            • 2011-06-06
            • 2011-03-03
            • 2010-09-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-14
            相关资源
            最近更新 更多