如何检查Python中是否存在具有给定pid的进程?


109

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

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


2
Windows是非标准操作系统。这些东西不是便携式的。知道您不能同时拥有两者,这是您的优先事项?选择一个优先级并编辑问题。
S.Lott

26
@ S.Lott Windows是非标准操作系统这是我在SO上看到的最愚蠢的话之一
Piotr Dobrogost

2
@Piotr Dobrogost:可以提供处理POSIX标准unix和非POSIX标准Windows的代码吗?如果是这样,请提供一个答案,即(a)解决了该问题,并且(b)明确表明Windows以某种方式兼容POSIX标准。
S.Lott

3
@PiotrDobrogost我认为S.Lott的言论更多地是关于实现细节和API支持,而不是市场份额。
罗伊·廷克

3
Windows与其他流行的操作系统之间的共性肯定比其他操作系统少。(进行Web开发的任何人都可以将它比作类似的臭名昭著的Microsoft产品。)但是响应@ S.Lott:我很少为Windows写Python代码,而该代码也不应该在Linux,OSX,BSD等系统上工作,所以我老实说,不要认为“优先选择”是有帮助的建议,尤其是因为Python尽可能地抽象出平台差异。
Michael Scheper 2015年

Answers:


162

如果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

3
在Linux和OSX上肯定可以工作,对于Windows我不能说。它不会按照您的要求终止进程,而是发送信号0,该信号基本上是“您在运行吗?”。
mluebke

10
在Windows上,这绝对不起作用,因为没有类似UNIX的信号。
亚历山大·列别捷夫

13
为了完整起见,您还应该检查错误号以确保其为3(捕获异常并检查第一个arg)。如果目标进程存在但您无权发送信号(无论出于何种原因),则这一点很重要。
haridsv'4


15
os.kill在Windows上受支持,但是os.kill(pid, 0)os.kill(pid, signal.CTRL_C_EVENT)可能终止进程(或失败)的相同。当我在子流程上尝试时,我得到了一个OSError位置errno==EINVAL
詹森·库姆斯

76

看一下psutil模块:

psutil(Python系统和进程实用程序)是一个跨平台的库,用于检索有关Python中正在运行的进程系统利用率(CPU,内存,磁盘,网络)的信息。[...]它目前支持32位64位体系结构的LinuxWindowsOSXFreeBSDSun Solaris,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)

以供参考:



我有一个实例,其中pid_exists在Windows上不再可靠。它误报了正在使用的死掉的pid。这提醒您不时更新psutil模块-尤其是在进行OS升级时。
戴蒙·布罗迪

62

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

(根据Jason R. Coombs评论编辑)

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
Jason R. Coombs

为什么?ESRCH的意思是“没有这样的过程”。
GiampaoloRodolà2012年

2
对。由于ESRCH的意思是“没有这样的过程”,所以errno!= ESRCH的意思是“没有这样的过程”或“过程不存在”,这与函数的名称非常相似。您特别提到了EPERM,但是其他可能的错误代码又意味着什么呢?挑出与检查意图松散相关的错误代码似乎不正确,而ESRCH似乎密切相关。
詹森·库姆斯

你是对的。我编辑了代码,现在可以反映您的建议。
GiampaoloRodolà2013年

python3中的os.kill()引发ProcessLookupError
martinkunev

19

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

为了绕过此限制,您可以检查是否/proc/<pid>存在:

import os

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

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


错误。PermissionError表示pid存在ProcessLookupError如果pid不存在,则得到。
jfs

OSError由于拒绝许可而导致的许可可以与其他许可区别开来-通过查看errno或捕获来自的更专业的PermissionError/ ProcessLookupError例外OSError。此外,仅当该进程存在时,您才会获得权限错误。因此,您的示例只是在Linux和其他Unices上运行的另一种方法,但是比正确调用并不完整os.kill(pid, 0)
maxschlepzig

此解决方案不是corss平台。OP希望它在Unix和Windows上可用。该/procprocfs仅在Linux上退出,甚至在BSD或OSX上都不退出。
查尔斯

如果将/ proc挂载hidepid = 2,此方法将失败,如果该进程由另一个用户拥有,则该进程将不会显示在列表中。
珀金斯

8

在Python 3.3+中,可以使用异常名称代替errno常量。Posix版本

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

7

在此处查找特定于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”替换为“名称”,并将其转换为“签入”列表以返回True或False。
JamesR

6

在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访问权限。
augustomen 2014年

请注意,该代码是错误的。GetExitCodeProcess接收一个句柄和一个指针,并且在此示例中,ExitCodeProcess当它仅是一个指针时,它将接收一个结构作为第二个参数。
Fabio Zadrozny

在OpenProcess之后,最好检查一下GetLastError。ERROR_ACCESS_DENIED表示该进程存在!这是使用此示例的完整示例:gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule

4

结合GiampaoloRodolà对POSIX的回答我对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并确保您甚至具有访问权限。
speedplane

kernel32.OpenProcess仅使用光还不够。如此指出的那样:“如果进程最近退出,则该句柄可能仍存在pid”。 如果kernel32.OpenProcess返回非零值,我们仍然需要kernel32.GetExitCodeProcess进一步检查退出代码。
2016年

2

在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;否则,返回true。否则,您返回False。OpenProcess文档:https : //msdn.microsoft.com/zh-cn/library/windows/desktop/ms684320%28v=vs.85%29.aspx


2

例如,如果您要检查banshee是否正在运行,该功能将在Linux上运行...(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的方法。您的方法需要一个进程名称。
maxschlepzig

-2

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


我应该更具体一些-我正在检查INVALIDITY。因此,我基本上希望能够查看是否未使用pid。
Evan Fosmark,2009年

1
但是你会怎么做呢?获得这些知识后的瞬间,某些东西可能会使用该pid。
Damien_The_Unbeliever,2009年

@Damien_The_Unbeliever-没关系,如果在我掌握了这些知识之后,某些东西正在使用它,那么我也明白你在说什么种族状况,但是我可以向你保证,它不适用于我的情况。
Evan Fosmark,2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.