Como verificar se existe um processo com um determinado pid em Python?

109

Existe uma maneira de verificar se um pid corresponde a um processo válido? Estou recebendo um pid de uma fonte diferente de os.getpid()e preciso verificar se um processo com esse pid não existe na máquina.

Eu preciso que ele esteja disponível no Unix e no Windows. Também estou verificando se o PID NÃO está em uso.

Evan Fosmark
fonte
2
O Windows é um sistema operacional não padrão. Esses tipos de coisas NÃO são portáteis. Saber que não pode ter os dois, qual é a sua prioridade? Escolha um como prioridade e edite a pergunta.
S.Lott de
26
@ S.Lott Windows é um sistema operacional não padrão Esta é uma das observações mais tolas que já vi no SO ...
Piotr Dobrogost
2
@Piotr Dobrogost: Você pode fornecer um código que lida com o padrão POSIX Unix e o Windows não POSIX padrão? Em caso afirmativo, forneça uma resposta que (a) resolva o problema e (b) deixe claro que o Windows é de alguma forma compatível com o padrão POSIX.
S.Lott
3
@PiotrDobrogost Acho que a observação de S.Lott foi mais sobre detalhes de implementação e suporte de API do que participação de mercado.
Roy Tinker,
3
O Windows certamente tem menos em comum com outros sistemas operacionais populares do que os demais. (Qualquer pessoa que faz desenvolvimento web pode compará-lo a um produto da Microsoft igualmente famoso.) Mas em resposta a @ S.Lott: Raramente escrevo código Python para Windows que não deva funcionar também em Linux, OSX, BSD, etc, então eu honestamente, não pense que 'escolher como prioridade' seja um conselho útil, especialmente porque o Python abstrai as diferenças de plataforma tanto quanto possível.
Michael Scheper

Respostas:

162

Enviar o sinal 0 para um pid levantará uma exceção OSError se o pid não estiver em execução e não fará nada de outra forma.

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
mluebke
fonte
3
Funciona com certeza em linux e OSX, não posso falar pelo Windows. Ele não mata o processo no sentido em que você está perguntando, ele envia o sinal 0, que é basicamente "Você está executando?".
mluebke de
10
Isso definitivamente não funciona no Windows, já que não existem sinais do tipo UNIX.
Alexander Lebedev
13
Para concluir, você também deve verificar o número do erro para ter certeza de que é 3 (pegue a exceção e verifique o primeiro argumento). Isso é importante se o processo de destino existe, mas você não tem permissão para enviar o sinal (por qualquer motivo).
Haridsv
8
Suportado pelo Windows agora. docs.python.org/library/os.html?highlight=os.kill#os.kill
michael
15
os.kill é compatível com Windows, mas os.kill(pid, 0)é o mesmo os.kill(pid, signal.CTRL_C_EVENT)que pode encerrar o processo (ou falhar). Eu recebo um OSErroronde errno==EINVALquando tento isso em um subprocesso.
Jason R. Coombs
76

Dê uma olhada no psutilmódulo:

psutil (sistema python e utilitários de processo) é uma biblioteca de plataforma cruzada para recuperar informações sobre processos em execução e utilização do sistema (CPU, memória, discos, rede) em Python. [...] Atualmente suporta Linux , Windows , OSX , FreeBSD e Sun Solaris , ambas arquiteturas de 32 e 64 bits , com versões Python de 2.6 a 3.4 (usuários de Python 2.4 e 2.5 podem usar a versão 2.1.3) . PyPy também funciona.

Ele tem uma função chamada pid_exists()que você pode usar para verificar se existe um processo com o pid fornecido.

Aqui está um exemplo:

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)

Para referência:

moooeeeep
fonte
Tive uma instância em que pid_exists deixou de ser confiável no Windows. Estava relatando erroneamente que pids mortos estavam em uso. Este é um lembrete para manter seu módulo psutil atualizado de tempos em tempos - especialmente ao fazer atualizações de sistema operacional.
Damon Brodie
62

O código mluebke não está 100% correto; kill () também pode aumentar EPERM (acesso negado), caso em que isso obviamente significa que existe um processo. Isso deve funcionar:

(editado de acordo com os comentários de 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

Você não pode fazer isso no Windows, a menos que use pywin32, ctypes ou um módulo de extensão C. Se você concordar em depender de uma biblioteca externa, poderá usar o psutil :

>>> import psutil
>>> psutil.pid_exists(2353)
True
Giampaolo Rodolà
fonte
haridsv sugere que o teste deve ser e.errno! = 3; talvez e.errno! = errno.ESRCH
Jason R. Coombs
Por quê? ESRCH significa "nenhum tal processo".
Giampaolo Rodolà
2
Certo. Visto que ESRCH significa "nenhum tal processo", errno! = ESRCH significa "nenhum tal processo" ou "processo existe", que é muito semelhante ao nome da função. Você mencionou especificamente EPERM, mas o que os outros códigos de erro possíveis implicam? Parece incorreto destacar um código de erro que está vagamente relacionado à intenção da verificação, enquanto ESRCH parece intimamente relacionado.
Jason R. Coombs
Você está certo. Eu editei o código que agora reflete o que você sugeriu.
Giampaolo Rodolà
em python3 os.kill () lança ProcessLookupError
martinkunev
19

As respostas envolvendo o envio de 'sinal 0' para o processo funcionarão apenas se o processo em questão pertencer ao usuário que está executando o teste . Caso contrário, você obterá um OSErrordevido às permissões , mesmo se o pid existir no sistema.

Para contornar essa limitação, você pode verificar se /proc/<pid>existe:

import os

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

Isso se aplica apenas a sistemas baseados em Linux, obviamente.

Omer Dagan
fonte
errado. PermissionErrorsignifica que pid existe , você obtém ProcessLookupErrorse pid não existir.
jfs
A OSErrorpermissão devido à negada pode ser diferenciada de outras - olhando para errno ou capturando as PermissionError/ ProcessLookupErrorexceções mais especializadas que derivam de OSError. Além disso, você só obterá o erro de permissão se o processo existir. Portanto, seu exemplo é apenas um método alternativo que funciona no Linux e alguns outros Unices, mas não é mais completo do que a chamada correta os.kill(pid, 0).
maxschlepzig
Esta solução não é uma plataforma corss. O OP deseja que ele esteja disponível em Unix e Windows. O /procprocfs só sai no Linux, nem mesmo no BSD ou OSX.
Charles
Este método falha se / proc for montado em hidepid = 2, o processo não será mostrado na lista se for propriedade de outro usuário.
Perkins
8

No Python 3.3+, você pode usar nomes de exceção em vez de constantes errno. Versão 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
jfs
fonte
7

Procure aqui uma maneira específica do Windows de obter uma lista completa dos processos em execução com seus IDs. Seria algo como

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]

Você pode então verificar o pid obtido nesta lista. Não tenho ideia sobre o custo de desempenho, então é melhor você verificar isso se for fazer a verificação pid com frequência.

Para * NIx, apenas use a solução do mluebke.

Alexander Lebedev
fonte
Isto funcionou bem para mim. Eu queria verificar se há um nome de processo, então troquei "ProcessID" por "Nome" e também o converti em uma verificação de na lista para retornar True ou False.
JamesR
6

Com base no ntrrgc, reforcei a versão do Windows para que ele verifique o código de saída do processo e verifique as permissões:

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
avião rápido
fonte
Na verdade, de acordo com msdn.microsoft.com/en-us/library/windows/desktop/… , GetExistCodeProcessrequer os direitos de acesso PROCESS_QUERY_INFORMATIONe PROCESS_QUERY_LIMITED_INFORMATION.
agosto,
Observe que o código está errado. GetExitCodeProcessrecebe um identificador e um ponteiro e neste exemplo está recebendo uma ExitCodeProcessestrutura como segundo parâmetro quando deveria ser apenas um ponteiro.
Fabio Zadrozny,
Depois de OpenProcess, é uma boa ideia verificar GetLastError. Um ERROR_ACCESS_DENIED significa que o processo existe! Aqui está um exemplo completo usando isso: gist.github.com/ociule/8a48d2a6b15f49258a87b5f55be29250
ociule
4

Combinando a resposta de Giampaolo Rodolà para POSIX e a minha para Windows , obtive o seguinte:

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
Alicia
fonte
A versão do windows não funciona para mim no windows 8.1. Você tem que verificar GetExitCodeProcesse ter certeza de que ainda tem acesso.
speedplane 01 de
Usar kernel32.OpenProcessapenas não é suficiente. Como apontado aqui , "se o processo foi encerrado recentemente, ainda pode existir um pid para o identificador." Se kernel32.OpenProcessretornar um valor diferente de zero, ainda precisamos kernel32.GetExitCodeProcessverificar o código de saída.
Miau de
2

No Windows, você pode fazer isso desta forma:

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

Em primeiro lugar, neste código você tenta obter um identificador para o processo com pid fornecido. Se o identificador for válido, feche o identificador para processo e retorne True; caso contrário, você retorna False. Documentação para OpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320%28v=vs.85%29.aspx

Andrei
fonte
2

Isso funcionará no Linux, por exemplo, se você quiser verificar se o banshee está rodando ... (o banshee é um reprodutor de música)

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")
BARSPIN
fonte
Este método é claramente inferior em comparação a usar os.kill(pid, 0)ou olhar /proc/{pid}. Em vez de executar um syscall, seu código bifurca um filho, executa um shell naquele filho, o shell interpreta seu mini-script de shell supérfluo, o shell bifurca outro filho que executa pgrep e, finalmente, pgrep itera /proc. Sua resposta não responde à pergunta postada. O OP pediu um método dado um PID. Seu método requer um nome de processo.
maxschlepzig
-2

Eu diria que use o PID para qualquer propósito que você estiver obtendo e lide com os erros normalmente. Caso contrário, é uma corrida clássica (o PID pode ser válido quando você verifica se é válido, mas vá embora um instante depois)

Damien_The_Unbeliever
fonte
Eu deveria ter sido mais específico - estou verificando a INVALIDEZ. Então, eu basicamente quero ser capaz de ver se um pid NÃO está em uso.
Evan Fosmark
1
Mas o que você fará com essa resposta? No instante depois que você ganhou esse conhecimento, algo pode usar esse pid.
Damien_The_Unbeliever
@Damien_The_Unbeliever - está tudo bem se algo está usando depois de eu obter esse conhecimento, e eu entendo o que você está dizendo sobre a condição de corrida, mas posso garantir que não se aplica à minha situação.
Evan Fosmark