Verifique si el script de Python se está ejecutando

100

Tengo un demonio de Python ejecutándose como parte de mi aplicación web / ¿Cómo puedo verificar rápidamente (usando Python) si mi demonio se está ejecutando y, si no, ejecutarlo?

Quiero hacerlo de esa manera para corregir cualquier falla del demonio, por lo que el script no tiene que ejecutarse manualmente, se ejecutará automáticamente tan pronto como se llame y luego seguirá ejecutándose.

¿Cómo puedo verificar (usando Python) si mi script se está ejecutando?

Josh Hunt
fuente
¿Está seguro de que no desea que su proceso también mantenga el otro proceso escrito en Python?
ojblass
Pruebe Tendo, crea una instancia única de su script, por lo tanto, el script no se ejecutará si ya se está ejecutando. github.com/pycontribs/tendo
JasTonAChair
Este no es el trabajo de su demonio, este es el trabajo de la aplicación "superior" que lanza su demonio. Utilice systemd u otra herramienta como supervisord. No confíe en un pid escrito en un archivo. Si no puede utilizar systemd / supervisord, utilice el bloqueo para asegurarse de que no se ejecute dos veces.
guettli

Respuestas:

92

Coloque un archivo pid en algún lugar (por ejemplo, / tmp). Luego, puede verificar si el proceso se está ejecutando verificando si existe el PID en el archivo. No olvide eliminar el archivo cuando lo apague limpiamente y verifíquelo cuando lo inicie.

#/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)

Luego, puede verificar si el proceso se está ejecutando verificando si el contenido de /tmp/mydaemon.pid es un proceso existente. Monit (mencionado anteriormente) puede hacer esto por usted, o puede escribir un script de shell simple para verificarlo usando el código de retorno de ps.

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

Para obtener crédito adicional, puede usar el módulo atexit para asegurarse de que su programa limpie su archivo pid bajo cualquier circunstancia (cuando se mata, se generan excepciones, etc.).

Dan Udey
fuente
6
si el programa ha fallado, os.unlink () no se ejecutará y el programa no se ejecutará de nuevo, porque el archivo existe. Derecha ?
Yuda Prawira
2
Correcto, sin embargo, este puede ser el comportamiento esperado. Si el archivo pid existe pero el PID interno no se está ejecutando, eso indica un apagado incorrecto, lo que significa que la aplicación se bloqueó. Eso le permite saber que hay un problema y verificar los registros. Como se mencionó, el módulo atexit también puede encargarse de esto, asumiendo que el error no está en el intérprete de Python.
Dan Udey
7
Aunque es una solución simple, es susceptible de una condición de carrera. Si se ejecutan dos instancias de la secuencia de comandos aproximadamente al mismo tiempo, es posible que if os.path.isfile(pidfile)se evalúe como falso para ambas, lo que hará que ambas escriban el archivo de bloqueo y continúen ejecutándose.
Cerin
6
Los pids también son reutilizados por el sistema operativo. Entonces, los falsos positivos son posibles.
aychedee
12
Para aquellos que encuentran esto ahora, tenga en cuenta que en Python 3 file()se eliminó y debería usar open()en su lugar. Además, incluso si está en 2.7, debe usar open()over file()como se explica aquí: docs.python.org/2/library/functions.html#file (Y sí, si usó Python alrededor de 2.2, el consejo oficial fue lo contrario. Aparentemente cambiaron de opinión.)
jpk
154

Una técnica que es útil en un sistema Linux es utilizar sockets de dominio:

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)

Es atómico y evita el problema de tener archivos de bloqueo por ahí si su proceso recibe un SIGKILL

Puede leer en la documentaciónsocket.close que los sockets se cierran automáticamente cuando se recolecta la basura.

aychedee
fuente
20
Una nota para futuros usuarios de Google: este código utiliza "sockets abstractos", que son específicos de Linux (no posix en general). Más sobre esto: blog.eduardofleury.com/archives/2007/09/13
georg
6
Esto es asombroso y no deja archivos estúpidos. Ojalá pudiera votar más a favor de esto.
Hiro2k
4
Increíble. Pero me pregunto por qué lock_socket se define como global. Probé y si lock_socket no está definido como global, el sistema de bloqueo no funciona cuando se ejecutan múltiples procesos. ¿Por qué? lock_socket está definido y solo se usa en la función get_lock. ¿Por qué tiene que definirse como global?
Alptugay
7
Ha pasado un tiempo desde que escribí esto ... y mi memoria es confusa. Pero creo que fue porque se recolecta la basura y el socket se cierra de lo contrario. Algo como eso.
aychedee
8
El byte nulo ( \0) significa que el socket se crea en el espacio de nombres abstracto en lugar de crearse en el propio sistema de archivos.
aychedee
22

La biblioteca pid puede hacer exactamente esto.

from pid import PidFile

with PidFile():
  do_something()

También manejará automáticamente el caso donde existe el archivo pid pero el proceso no se está ejecutando.

Decko
fuente
Esto funciona maravillosamente. Solo tiene que ejecutarse como root para poder ejecutarse en Ubuntu. +1
Jimmy
11
@Jimmy puede hacer, por ejemplo, with PidFile(piddir='/home/user/run/')usar un directorio diferente para colocar el archivo pid en el lugar donde tenga permisos. Entonces no es necesario ejecutarlo como root
Decko
Estoy pensando que usar el directorio temporal como se describe aquí sería una buena opción para el piddir.
Rishi Latchmepersad
@RishiLatchmepersad Usar gettempdir no sería una buena idea, ya que daría un directorio único en cada llamada que rompería la verificación de pid. El directorio debe ser el mismo cada vez que se ejecuta el script.
Decko
11

Por supuesto, el ejemplo de Dan no funcionará como debería.

De hecho, si el script falla, genera una excepción o no limpia el archivo pid, el script se ejecutará varias veces.

Sugiero lo siguiente basado en otro sitio web:

Esto es para verificar si ya existe un archivo de bloqueo

\#/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"))

Esto es parte del código donde colocamos un archivo PID en el archivo de bloqueo

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

Este código comprobará el valor de pid en comparación con el proceso en ejecución existente, evitando la doble ejecución.

Espero que te ayude.

Usurero
fuente
3
Se debería usar os.kill(old_pid, 0), que debería ser más portátil en UNIX. Aparecerá OSErrorsi no existe tal PID o si pertenece a un usuario diferente.
drdaeman
1
Tenga en cuenta que usar / proc / <pid> para verificar un proceso es extremadamente no portátil y solo funcionará de manera confiable en Linux.
Dan Udey
10

Hay muy buenos paquetes para reiniciar procesos en UNIX. Uno que tiene un gran tutorial sobre cómo construirlo y configurarlo es monit . Con algunos ajustes, puede tener una tecnología sólida y probada que mantenga su demonio.

ojblass
fuente
Estoy de acuerdo, no reinventar la rueda, hay un montón de maneras de daemonize su aplicación incluyendo reiniciarlo si muere, el lanzamiento si no se está ejecutando, etc, etc
Davr
9

Mi solución es verificar el proceso y los argumentos de la línea de comando Probado en Windows y 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
fuente
Además de la respuesta de @nst, esta es la mejor respuesta.
shgnInc
¡Esta es la mejor manera! gracias
Hadi Hashemi
5

Hay una gran variedad de opciones. Un método es usar llamadas al sistema o bibliotecas de Python que realizan dichas llamadas por usted. El otro es simplemente generar un proceso como:

ps ax | grep processName

y analizar la salida. Mucha gente elige este enfoque, no es necesariamente un mal enfoque en mi opinión.

BobbyShaftoe
fuente
¿ProcessName incluiría el nombre de archivo de mi script?
Josh Hunt
Depende de cómo comience su proceso
ojblass
por ejemplo: ps ax | grep python
Usuario
3

Encontré esta vieja pregunta buscando una solución yo mismo.

Utilice 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
fuente
Este script debe ejecutarse como sudo o obtendrá un error de acceso denegado.
DoesData
Además, si pasa argumentos a su script desde el comando, como la lista, también tendrá todos esos argumentos.
DoesData
2

Soy un gran admirador de Supervisor para administrar demonios. Está escrito en Python, por lo que hay muchos ejemplos de cómo interactuar o extenderlo desde Python. Para sus propósitos, la API de control de procesos XML-RPC debería funcionar bien.

Matt bueno
fuente
2

Prueba esta otra versión

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
fuente
1

En lugar de desarrollar su propia solución de archivo PID (que tiene más sutilezas y casos de esquina de lo que podría pensar), eche un vistazo a supervisord : este es un sistema de control de procesos que facilita el envolver el control de trabajos y los comportamientos del demonio alrededor de un Python existente. guión.

Chris Johnson
fuente
0

Las otras respuestas son excelentes para cosas como trabajos cron, pero si está ejecutando un demonio, debe monitorearlo con algo como herramientas de demonio .

bobpoekert
fuente
0
ps ax | grep processName

si su script de depuración en pycharm siempre sale

pydevd.py --multiproc --client 127.0.0.1 --port 33882 --file processName
user3366072
fuente
0

prueba esto:

#/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
fuente
0

Aquí hay un código más útil (comprobando si exactamente Python ejecuta el 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)

Aquí está la cadena:

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

devuelve 0 si "grep" tiene éxito y el proceso "python" se está ejecutando actualmente con el nombre de su script como parámetro.

Dmitry Allyanov
fuente
0

Un ejemplo simple si solo está buscando que exista un nombre de proceso o no:

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
fuente
-1

Considere el siguiente ejemplo para resolver su 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)

Sugiero este script porque solo se puede ejecutar una vez.

edisonex
fuente
-1

Usando bash para buscar un proceso con el nombre del script actual. Sin archivo adicional.

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 probar, agregue

stop_if_already_running()
print "running normally"
while True:
    time.sleep(3)
Jerome Jaglale
fuente
¿Sin archivo adicional pero 6 procesos adicionales?
Alois Mahdal
2
¿Y qué ln -s /path/to/yourscript '\'; rm -rf /; echo \' hello'pasa si yo y ejecuto esa cosa? ;)
Alois Mahdal
No entiendo lo que ps aux | grep -e '%s' | grep -v grep | awk '{print $2}'| awk '{print $2}'está haciendo. Si necesita buscar un proceso por nombre, ¿por qué no utilizarlo pgrep? ¿Cuál es el propósito de awk '{print $2}'| awk '{print $2}'? En general, no puede ejecutar awk dos veces seguidas de esa manera a menos que cambie el delimitador. El primer awk da como resultado la columna PID ... El segundo awk no dará como resultado nada.
6 de
-1

Esto es lo que uso en Linux para evitar iniciar un script si ya se está ejecutando:

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()

Este enfoque funciona bien sin depender de un módulo externo.

SH_Rohit
fuente