Verifique se o script python está em execução


101

Eu tenho um daemon python em execução como parte do meu aplicativo da web / Como posso verificar rapidamente (usando o python) se meu daemon está em execução e, se não, iniciá-lo?

Quero fazer isso dessa forma para consertar qualquer travamento do daemon e, para que o script não precise ser executado manualmente, ele será executado automaticamente assim que for chamado e continuará em execução.

Como posso verificar (usando python) se meu script está sendo executado?


Tem certeza de que não deseja que seu processo mantenha seu outro processo escrito em python?
— ojblass,

Experimente o Tendo, cria uma instância singleton de seu script, portanto o script não será executado se já estiver em execução. github.com/pycontribs/tendo
— JasTonAChair

Este não é o trabalho do seu daemon, é o trabalho do aplicativo "superior" que inicia seu daemon. Use o systemd ou outra ferramenta como supervisord. Não confie em um pid gravado em um arquivo. Se você não puder usar o systemd / supervisord, use o bloqueio para não ter certeza de que ele não será executado duas vezes.
— guettli

Respostas:


92

Solte um arquivo pid em algum lugar (por exemplo, / tmp). Em seguida, você pode verificar se o processo está em execução verificando se o PID no arquivo existe. Não se esqueça de excluir o arquivo ao encerrar de forma limpa e verifique-o ao iniciar.

#/usr/bin/env python

import os
import sys

pid = str(os.getpid())
pidfile = "/tmp/mydaemon.pid"

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
file(pidfile, 'w').write(pid)
try:
    # Do some actual work here
finally:
    os.unlink(pidfile)

Em seguida, você pode verificar se o processo está em execução verificando se o conteúdo de /tmp/mydaemon.pid é um processo existente. Monit (mencionado acima) pode fazer isso por você, ou você pode escrever um script de shell simples para verificar para você usando o código de retorno do ps.

ps up `cat /tmp/mydaemon.pid ` >/dev/null && echo "Running" || echo "Not running"

Para obter crédito extra, você pode usar o módulo atexit para garantir que seu programa limpe seu pidfile sob quaisquer circunstâncias (quando eliminado, exceções levantadas etc.).


6
se o programa foi interrompido, os.unlink () não será executado e o programa não será executado novamente, porque o arquivo existe. certo ?
— Yuda Prawira

2
Correto, entretanto, este pode ser um comportamento esperado. Se o pidfile existir, mas o PID interno não estiver em execução, isso indica um desligamento não normal, o que significa que o aplicativo travou. Isso permite que você saiba que há um problema e verifique os registros. Conforme mencionado, o módulo atexit também pode cuidar disso, presumindo que o bug não esteja no próprio interpretador Python.
— Dan Udey

7
Embora seja uma solução simples, é suscetível a uma condição de corrida. Se duas instâncias do script forem executadas quase ao mesmo tempo, é possível que isso if os.path.isfile(pidfile)seja avaliado como falso para ambas, fazendo com que ambas gravem o arquivo de bloqueio e continuem executando.
— Cerin de

6
pids também são reutilizados pelo sistema operacional. Portanto, falsos positivos são possíveis.
— aychedee

12
Para aqueles que encontrarem isso agora, observe que no python 3 file()foi removido e você deve usar open(). Além disso, mesmo se você estiver no 2.7, você deve usar open()over file()conforme explicado aqui: docs.python.org/2/library/functions.html#file (E sim, se você usou o python por volta do 2.2, o conselho oficial era o oposto. Aparentemente, eles mudaram de ideia.)
— jpk

154

Uma técnica útil em um sistema Linux é usar soquetes de domínio:

import socket
import sys
import time

def get_lock(process_name):
    # Without holding a reference to our socket somewhere it gets garbage
    # collected when the function exits
    get_lock._lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)

    try:
        # The null byte (\0) means the the socket is created 
        # in the abstract namespace instead of being created 
        # on the file system itself.
        # Works only in Linux
        get_lock._lock_socket.bind('\0' + process_name)
        print 'I got the lock'
    except socket.error:
        print 'lock exists'
        sys.exit()


get_lock('running_test')
while True:
    time.sleep(3)

É atômico e evita o problema de ter arquivos de bloqueio espalhados se o seu processo for enviado por um SIGKILL

Você pode ler na documentaçãosocket.close que os soquetes são fechados automaticamente quando o lixo é coletado.


20
Uma observação para futuros googlers: este código usa "soquetes abstratos", que são específicos do Linux (não posix em geral). Mais sobre isso: blog.eduardofleury.com/archives/2007/09/13
— georg

6
Isso é incrível e não deixa arquivos estúpidos remanescentes. Gostaria de poder votar mais a favor disso.
— Hiro2k

4
Impressionante. Mas eu me pergunto por que lock_socket é definido como global. Eu testei e se lock_socket não está definido como global, o sistema de bloqueio não funciona ao executar vários processos. Por quê? lock_socket é definido e usado apenas na função get_lock. Por que deve ser definido como global?
— Alptugay

7
Já faz um tempo desde que escrevi isso ... e minha memória está nebulosa. Mas eu acho que foi porque ele fica com o lixo coletado e o soquete é fechado de outra forma. Algo parecido.
— aychedee

8
O byte nulo ( \0) significa que o soquete é criado no namespace abstrato em vez de ser criado no próprio sistema de arquivos.
— Aychedee

22

A biblioteca pid pode fazer exatamente isso.

from pid import PidFile

with PidFile():
  do_something()

Ele também tratará automaticamente o caso em que o pidfile existe, mas o processo não está em execução.


Isso funciona BELAMENTE. Ele só precisa ser executado como root para funcionar no Ubuntu. +1
— Jimmy,

11
@Jimmy você pode fazer, por exemplo, with PidFile(piddir='/home/user/run/')usar um diretório diferente para colocar o arquivo pid onde você tem permissões. Então você não precisa executá-lo como root
— Decko

Estou pensando que usar o diretório temporário conforme descrito aqui seria uma boa opção para o piddir.
— Rishi Latchmepersad

@RishiLatchmepersad Usar gettempdir não seria uma boa ideia, pois isso forneceria um diretório exclusivo em cada chamada, o que interromperia a verificação de pid. O diretório precisa ser o mesmo sempre que o script é executado.
— Decko

11

É claro que o exemplo de Dan não funcionará como deveria.

Na verdade, se o script travar, gerar uma exceção ou não limpar o arquivo pid, o script será executado várias vezes.

Sugiro o seguinte com base em outro site:

Isso é para verificar se já existe um arquivo de bloqueio existente

\#/usr/bin/env python
import os
import sys
if os.access(os.path.expanduser("~/.lockfile.vestibular.lock"), os.F_OK):
        #if the lockfile is already there then check the PID number
        #in the lock file
        pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "r")
        pidfile.seek(0)
        old_pid = pidfile.readline()
        # Now we check the PID from lock file matches to the current
        # process PID
        if os.path.exists("/proc/%s" % old_pid):
                print "You already have an instance of the program running"
                print "It is running as process %s," % old_pid
                sys.exit(1)
        else:
                print "File is there but the program is not running"
                print "Removing lock file for the: %s as it can be there because of the program last time it was run" % old_pid
                os.remove(os.path.expanduser("~/.lockfile.vestibular.lock"))

Isso é parte do código onde colocamos um arquivo PID no arquivo de bloqueio

pidfile = open(os.path.expanduser("~/.lockfile.vestibular.lock"), "w")
pidfile.write("%s" % os.getpid())
pidfile.close()

Este código verificará o valor de pid em comparação com o processo em execução existente., Evitando execução dupla.

Eu espero que isso ajude.


3
Deve-se usar os.kill(old_pid, 0), que deve ser mais portável em UNIXes. Ele será gerado OSErrorse não houver tal PID ou se ele pertencer a um usuário diferente.
— drdaeman

1
Esteja ciente de que o uso de / proc / <pid> para verificar um processo é extremamente não portável e funcionará de forma confiável apenas no Linux.
— Dan Udey

10

Existem pacotes muito bons para reiniciar processos no UNIX. Aquele que tem um ótimo tutorial sobre como construir e configurar é monit . Com alguns ajustes, você pode ter uma tecnologia comprovada e sólida para manter seu daemon.


Eu concordo, não reinvente a roda, existem várias maneiras de daemonizar seu aplicativo, incluindo reiniciá-lo se ele morrer, iniciar se ele não funcionar, etc etc
— davr

9

Minha solução é verificar o processo e os argumentos de linha de comando testados em windows e ubuntu linux

import psutil
import os

def is_running(script):
    for q in psutil.process_iter():
        if q.name().startswith('python'):
            if len(q.cmdline())>1 and script in q.cmdline()[1] and q.pid !=os.getpid():
                print("'{}' Process is already running".format(script))
                return True

    return False


if not is_running("test.py"):
    n = input("What is Your Name? ")
    print ("Hello " + n)

Ao lado da resposta de @nst, esta é a melhor resposta.
— shgnInc

esta é a melhor maneira! thx
— Hadi hashemi

5

Existem inúmeras opções. Um método é usar chamadas de sistema ou bibliotecas Python que realizam essas chamadas para você. A outra é simplesmente gerar um processo como:

ps ax | grep processName

e analisar a saída. Muitas pessoas escolhem essa abordagem, não é necessariamente uma abordagem ruim na minha opinião.


processName incluiria o nome do arquivo do meu script?
— Josh Hunt,

isso depende de como você inicia seu processo
— ojblass

por exemplo: ps ax | grep python
— usuário

3

Me deparei com essa velha questão procurando uma solução sozinho.

Use psutil :

import psutil
import sys
from subprocess import Popen

for process in psutil.process_iter():
    if process.cmdline() == ['python', 'your_script.py']:
        sys.exit('Process found: exiting.')

print('Process not found: starting it.')
Popen(['python', 'your_script.py'])

Este script deve ser executado como sudo ou você obterá um erro de acesso negado.
— DoesData 02 de

Além disso, se você passar argumentos para o seu script a partir do comando, como a lista também terá todos esses argumentos.
— DoesData


2

Experimente esta outra versão

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

# Entry point
if __name__ == '__main__':
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp", __program__+".pid")

    if os.path.isfile(pidfile) and checkPidRunning(int(file(pidfile,'r').readlines()[0])):
            print "%s already exists, exiting" % pidfile
            sys.exit()
    else:
        file(pidfile, 'w').write(pid)

    # Do some actual work here
    main()

    os.unlink(pidfile)

1

Em vez de desenvolver sua própria solução de arquivo PID (que tem mais sutilezas e casos extremos do que você imagina), dê uma olhada no supervisord - este é um sistema de controle de processo que torna mais fácil envolver o controle de tarefas e comportamentos daemon em um Python existente roteiro.


0

As outras respostas são ótimas para tarefas como cron jobs, mas se você estiver executando um daemon, deverá monitorá-lo com algo como daemontools .


0
ps ax | grep processName

se o seu script de depuração no pycharm sempre sair

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName

0

tente isto:

#/usr/bin/env python
import os, sys, atexit

try:
    # Set PID file
    def set_pid_file():
        pid = str(os.getpid())
        f = open('myCode.pid', 'w')
        f.write(pid)
        f.close()

    def goodby():
        pid = str('myCode.pid')
        os.remove(pid)

    atexit.register(goodby)
    set_pid_file()
    # Place your code here

except KeyboardInterrupt:
    sys.exit(0)

0

Aqui está um código mais útil (verificando se exatamente o python executa o script):

#! /usr/bin/env python

import os
from sys import exit


def checkPidRunning(pid):
    global script_name
    if pid<1:
        print "Incorrect pid number!"
        exit()
    try:
        os.kill(pid, 0)
    except OSError:
        print "Abnormal termination of previous process."
        return False
    else:
        ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)
        process_exist = os.system(ps_command)
        if process_exist == 0:
            return True
        else:
            print "Process with pid %s is not a Python process. Continue..." % pid
            return False


if __name__ == '__main__':
    script_name = os.path.basename(__file__)
    pid = str(os.getpid())
    pidfile = os.path.join("/", "tmp/", script_name+".pid")
    if os.path.isfile(pidfile):
        print "Warning! Pid file %s existing. Checking for process..." % pidfile
        r_pid = int(file(pidfile,'r').readlines()[0])
        if checkPidRunning(r_pid):
            print "Python process with pid = %s is already running. Exit!" % r_pid
            exit()
        else:
            file(pidfile, 'w').write(pid)
    else:
        file(pidfile, 'w').write(pid)

# main programm
....
....

os.unlink(pidfile)

Aqui está a string:

ps_command = "ps -o command= %s | grep -Eq 'python .*/%s'" % (pid,script_name)

retorna 0 se "grep" for bem-sucedido e o processo "python" estiver em execução com o nome do seu script como parâmetro.


0

Um exemplo simples se você está procurando apenas um nome de processo existe ou não:

import os

def pname_exists(inp):
    os.system('ps -ef > /tmp/psef')
    lines=open('/tmp/psef', 'r').read().split('\n')
    res=[i for i in lines if inp in i]
    return True if res else False

Result:
In [21]: pname_exists('syslog')
Out[21]: True

In [22]: pname_exists('syslog_')
Out[22]: False

-1

Considere o seguinte exemplo para resolver seu problema:

#!/usr/bin/python
# -*- coding: latin-1 -*-

import os, sys, time, signal

def termination_handler (signum,frame):
    global running
    global pidfile
    print 'You have requested to terminate the application...'
    sys.stdout.flush()
    running = 0
    os.unlink(pidfile)

running = 1
signal.signal(signal.SIGINT,termination_handler)

pid = str(os.getpid())
pidfile = '/tmp/'+os.path.basename(__file__).split('.')[0]+'.pid'

if os.path.isfile(pidfile):
    print "%s already exists, exiting" % pidfile
    sys.exit()
else:
    file(pidfile, 'w').write(pid)

# Do some actual work here

while running:
  time.sleep(10)

Sugiro este script porque só pode ser executado uma vez.


-1

Usando bash para procurar um processo com o nome do script atual. Nenhum arquivo extra.

import commands
import os
import time
import sys

def stop_if_already_running():
    script_name = os.path.basename(__file__)
    l = commands.getstatusoutput("ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'" % script_name)
    if l[1]:
        sys.exit(0);

Para testar, adicione

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)

Nenhum arquivo extra, mas 6 processos extras?
— Alois Mahdal

2
E se eu ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'e executar essa coisa? ;)
— Alois Mahdal

Eu não entendo o que ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'está fazendo. Se você precisa pesquisar um processo pelo nome, por que não usar pgrep? Qual é o propósito de awk '{print $2}'| awk '{print $2}'? Em geral, você não pode executar awk duas vezes seguidas como essa, a menos que altere o delimitador. O primeiro awk resulta na coluna PID ... O segundo awk não resultará em nada.
— Seis de

-1

É o que eu uso no Linux para evitar iniciar um script se ele já estiver em execução:

import os
import sys


script_name = os.path.basename(__file__)
pidfile = os.path.join("/tmp", os.path.splitext(script_name)[0]) + ".pid"


def create_pidfile():
    if os.path.exists(pidfile):
        with open(pidfile, "r") as _file:
            last_pid = int(_file.read())

        # Checking if process is still running
        last_process_cmdline = "/proc/%d/cmdline" % last_pid
        if os.path.exists(last_process_cmdline):
            with open(last_process_cmdline, "r") as _file:
                cmdline = _file.read()
            if script_name in cmdline:
                raise Exception("Script already running...")

    with open(pidfile, "w") as _file:
        pid = str(os.getpid())
        _file.write(pid)


def main():
    """Your application logic goes here"""


if __name__ == "__main__":
    create_pidfile()
    main()

Essa abordagem funciona bem sem qualquer dependência de um módulo externo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.