python - saber - Asegurar una sola instancia de una aplicación en Linux
instalar python en linux (12)
Estoy trabajando en una aplicación de interfaz gráfica de usuario en WxPython, y no estoy seguro de cómo puedo asegurar que solo se ejecute una copia de mi aplicación en un momento dado en la máquina. Debido a la naturaleza de la aplicación, ejecutar más de una vez no tiene sentido y fallará rápidamente. En Win32, puedo simplemente crear un mutex con nombre y verificarlo al inicio. Desafortunadamente, no conozco ninguna instalación en Linux que pueda hacer esto.
Estoy buscando algo que se lanzará automáticamente si la aplicación falla inesperadamente. No quiero tener que cargar a mis usuarios con tener que eliminar manualmente los archivos de bloqueo porque me he caído.
¿Puedes usar la utilidad ''pidof''? Si su aplicación se está ejecutando, pidof escribirá el ID de proceso de su aplicación en stdout. De lo contrario, imprimirá una nueva línea (LF) y devolverá un código de error.
Ejemplo (de bash, por simplicidad):
linux# pidof myapp
8947
linux# pidof nonexistent_app
linux#
Aquí está la solución basada en el puerto TCP:
# Use a listening socket as a mutex against multiple invocations
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((''127.0.0.1'', 5080))
s.listen(1)
Busque un módulo de python que interactúe con semáforos SYSV en Unix. Los semáforos tienen una bandera SEM_UNDO que hará que se liberen los recursos mantenidos por el proceso a si el proceso falla.
De lo contrario, como sugirió Bernard, puedes usar
import os
os.getpid()
Y escríbalo en / var / run / application_name .pid. Cuando se inicia el proceso, debe verificar si el pid en / var / run / application_name .pid está listado en la tabla ps y salir si lo está, de lo contrario escriba su propio pid en / var / run / application_name .pid. En el siguiente var_run_pid está el pid que lees desde / var / run / application_name .pid
cmd = "ps -p %s -o comm=" % var_run_pid
app_name = os.popen(cmd).read().strip()
if len(app_name) > 0:
Already running
Con mucho, el método más común es soltar un archivo en / var / run / called [application] .pid que contiene solo el PID del proceso en ejecución, o proceso principal. Como alternativa, puede crear un conducto con nombre en el mismo directorio para poder enviar mensajes al proceso activo, por ejemplo, para abrir un archivo nuevo.
Esto se basa en la answer del usuario . Principalmente aborda una complicada preocupación que tiene que ver con el acceso de escritura al archivo de bloqueo. En particular, si el archivo de bloqueo fue creado primero por root
, otro usuario foo
podrá intentar reescribir este archivo debido a la ausencia de permisos de escritura para el usuario foo
. La solución obvia parece ser crear el archivo con permisos de escritura para todos. Esta solución también se basa en una answer diferente por mi parte, teniendo que crear un archivo con dichos permisos personalizados. Esta preocupación es importante en el mundo real, donde su programa puede ser ejecutado por cualquier usuario, incluida la root
.
import fcntl, os, stat, tempfile
app_name = ''myapp'' # <-- Customize this value
# Establish lock file settings
lf_name = ''.{}.lock''.format(app_name)
lf_path = os.path.join(tempfile.gettempdir(), lf_name)
lf_flags = os.O_WRONLY | os.O_CREAT
lf_mode = stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH # This is 0o222, i.e. 146
# Create lock file
# Regarding umask, see https://.com/a/15015748/832230
umask_original = os.umask(0)
try:
lf_fd = os.open(lf_path, lf_flags, lf_mode)
finally:
os.umask(umask_original)
# Try locking the file
try:
fcntl.lockf(lf_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
msg = (''Error: {} may already be running. Only one instance of it ''
''can run at a time.''
).format(''appname'')
exit(msg)
Una limitación del código anterior es que si el archivo de bloqueo ya existía con permisos inesperados, esos permisos no se corregirán.
Me hubiera gustado usar /var/run/<appname>/
como el directorio para el archivo de bloqueo, pero la creación de este directorio requiere permisos de root
. Puede tomar su propia decisión sobre qué directorio usar.
Tenga en cuenta que no es necesario abrir un identificador de archivo para el archivo de bloqueo.
He creado un marco básico para ejecutar este tipo de aplicaciones cuando desea poder pasar los argumentos de la línea de comando de las instancias subsiguientes intentadas al primero. Una instancia comenzará a escuchar en un puerto predefinido si no encuentra una instancia que ya esté escuchando allí. Si ya existe una instancia, envía sus argumentos de línea de comando sobre el socket y sale.
Lo correcto es el bloqueo de aviso mediante flock(LOCK_EX)
; en Python, esto se encuentra en el módulo fcntl
.
A diferencia de los archivos pidfiles, estos bloqueos siempre se liberan automáticamente cuando el proceso muere por cualquier motivo, no existen condiciones de carrera relacionadas con la eliminación de archivos (ya que el archivo no necesita ser eliminado para liberar el bloqueo), y no hay posibilidad de que sea diferente. proceso heredando el PID y apareciendo para validar un bloqueo obsoleto.
Si desea una detección de cierre no limpia, puede escribir un marcador (como su PID, para los tradicionalistas) en el archivo después de agarrar el bloqueo, y luego truncar el archivo al estado de 0 bytes antes de un apagado limpio (mientras se mantiene el bloqueo) ); por lo tanto, si no se mantiene el bloqueo y el archivo no está vacío, se indica un cierre no limpio.
Si crea un archivo de bloqueo y coloca el pid en él, puede verificar el ID de su proceso y decir si se colgó, ¿no?
No lo he hecho personalmente, así que tómelo con las cantidades adecuadas de sal. :pag
Solución de bloqueo completa utilizando el módulo fcntl
:
import fcntl
pid_file = ''program.pid''
fp = open(pid_file, ''w'')
try:
fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
# another instance is running
sys.exit(1)
wxWidgets ofrece una clase wxSingleInstanceChecker para este propósito: wxPython doc o wxWidgets doc . El documento wxWidgets tiene código de muestra en C ++, pero el equivalente de python debería ser algo como esto (no probado):
name = "MyApp-%s" % wx.GetUserId()
checker = wx.SingleInstanceChecker(name)
if checker.IsAnotherRunning():
return False
El conjunto de funciones definidas en semaphore.h
- sem_open()
, sem_trywait()
, etc. - son equivalentes a POSIX, creo.
Hay varias técnicas comunes que incluyen el uso de semáforos. El que veo más utilizado es crear un "archivo de bloqueo pid" en el inicio que contiene el pid del proceso en ejecución. Si el archivo ya existe cuando se inicia el programa, ábralo y agarre el pid dentro, verifique si se está ejecutando un proceso con ese pid, si es que comprueba el valor de la línea de comando en / proc / pid para ver si es un instancia de su programa, si se cierra, de lo contrario sobrescriba el archivo con su pid. El nombre habitual para el archivo pid es application_name .pid
.