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?

Josh Hunt
fonte
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.).

Dan Udey
fonte
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.

Aychedee
fonte
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.

Decko
fonte
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.

Shylock
fonte
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.

ojblass
fonte
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)
kabapy
fonte
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.

BobbyShaftoe
fonte
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'])
NST
fonte
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

Sou um grande fã do Supervisor para gerenciamento de demônios. É escrito em Python, portanto, existem muitos exemplos de como interagir ou estendê-lo a partir de Python. Para seus propósitos, a API de controle de processo XML-RPC deve funcionar bem.

Matt Good
fonte
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)
debuti
fonte
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.

Chris Johnson
fonte
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 .

Bobpoekert
fonte
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
user3366072
fonte
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)
MrRolling
fonte
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.

Dmitry Allyanov
fonte
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
Tomba
fonte
-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.

edisonex
fonte
-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)
Jerome Jaglale
fonte
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.

SH_Rohit
fonte