Question

Je travaille sur une application graphique dans WxPython et je ne suis pas sûr de savoir comment je peux m'assurer qu'une seule copie de mon application est exécutée à la fois sur la machine. En raison de la nature de l'application, une exécution plus d'une fois n'a aucun sens et échouera rapidement. Sous Win32, je peux simplement créer un mutex nommé et le vérifier au démarrage. Malheureusement, je ne connais aucune installation sous Linux capable de le faire.

Je recherche quelque chose qui sera automatiquement publié en cas de blocage inattendu de l'application. Je ne veux pas surcharger mes utilisateurs de la nécessité de supprimer manuellement les fichiers verrouillés parce que je me suis écrasé.

Était-ce utile?

La solution

Il existe plusieurs techniques courantes, notamment l’utilisation de sémaphores. Celui que je vois le plus souvent utilisé consiste à créer un "fichier de verrouillage pid". au démarrage qui contient le pid du processus en cours. Si le fichier existe déjà au démarrage du programme, ouvrez-le et saisissez le pid à l'intérieur, vérifiez si un processus avec ce pid est en cours d'exécution, s'il vérifie la valeur de la ligne de commande dans / proc / pid pour voir s’il s’agit d’une instance de votre programme, si elle est alors fermée, sinon écrasez le fichier avec votre pid. Le nom usuel du fichier pid est nom_application .pid .

Autres conseils

The Right Thing est un verrouillage consultatif utilisant flock (LOCK_EX) ; en Python, cela se trouve dans le module fcntl .

Contrairement aux pidfiles, ces verrous sont toujours automatiquement libérés lorsque votre processus décède, quelle que soit la raison, aucune condition de concurrence n’existe en ce qui concerne la suppression de fichier (car le fichier n’a pas besoin d'être supprimé pour que le lock), et il n'y a aucune chance qu'un processus différent hérite du PID et apparaisse ainsi pour valider un verrou périmé.

Si vous souhaitez une détection d’arrêt malpropre, vous pouvez écrire un marqueur (tel que votre PID, pour les traditionalistes) dans le fichier après avoir saisi le verrou, puis tronquer le fichier à l’état 0 octet avant un arrêt complet (pendant le verrouillage). est détenu); ainsi, si le verrou n'est pas maintenu et que le fichier n'est pas vide, un arrêt impropre est indiqué.

Solution de verrouillage complète à l'aide du module 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 propose une classe wxSingleInstanceChecker à cette fin: wxPython doc ou doc wxWidgets . La doc wxWidgets a un exemple de code en C ++, mais l'équivalent en python devrait ressembler à ceci (non testé):

  name = "MyApp-%s" % wx.GetUserId()
  checker = wx.SingleInstanceChecker(name)
  if checker.IsAnotherRunning():
      return False

Ceci s'appuie sur la réponse de l'utilisateur zgoda . Il s’agit principalement d’un problème délicat concernant l’accès en écriture au fichier de verrouillage. En particulier, si le fichier de verrouillage a d'abord été créé par root , un autre utilisateur foo ne peut alors plus tenter de réécrire ce fichier avec succès en raison de l'absence d'autorisations en écriture pour l'utilisateur < code> foo . La solution évidente semble être de créer le fichier avec des autorisations en écriture pour tout le monde. Cette solution repose également sur une réponse différente de ma part , car elle doit créer un fichier avec de telles autorisations personnalisées. Cette préoccupation est importante dans le monde réel où votre programme peut être exécuté par tout utilisateur, y compris racine .

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://stackoverflow.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)

Une limitation du code ci-dessus est que si le fichier de verrouillage existait déjà avec des autorisations inattendues, ces autorisations ne seront pas corrigées.

J'aurais aimé utiliser / var / run / , comme répertoire du fichier verrou, mais la création de ce répertoire nécessite des autorisations racine . Vous pouvez choisir vous-même le répertoire à utiliser.

Notez qu'il n'est pas nécessaire d'ouvrir un descripteur de fichier sur le fichier verrou.

Voici la solution basée sur le port 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)

Recherchez un module python qui s'interface avec les sémaphores SYSV sous unix. Les sémaphores ont un indicateur SEM_UNDO qui provoquera la libération des ressources détenues par le processus a si le processus se bloque.

Sinon, comme l'a suggéré Bernard, vous pouvez utiliser

import os
os.getpid()

Et écrivez-le dans /var/run/ nom_application.pid. Lorsque le processus démarre, il doit vérifier si le pid dans /var/run/ nom_application.pid est répertorié dans la table ps et quitter si c'est le cas. Sinon, écrivez son propre pid dans / var / run / nom_application .pid. Var_run_pid est le pid que vous avez lu dans / var / run / nom_application .pid

cmd = "ps -p %s -o comm=" % var_run_pid
app_name = os.popen(cmd).read().strip()
if len(app_name) > 0:
    Already running

L'ensemble des fonctions définies dans semaphore.h - sem_open () , sem_trywait () , etc. - sont l'équivalent POSIX, je crois.

Si vous créez un fichier de verrouillage et y placez le pid, vous pouvez vérifier l'id de votre processus par rapport à celui-ci et indiquer si vous êtes bloqué, non?

Je n'ai pas fait cela personnellement, alors prenez-le avec les quantités appropriées de sel. : p

Pouvez-vous utiliser l'utilitaire 'pidof'? Si votre application est en cours d'exécution, pidof écrira l'ID de processus de votre application sur stdout. Sinon, il imprimera une nouvelle ligne (LF) et retournera un code d'erreur.

Exemple (de bash, par souci de simplicité):

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#

De loin, la méthode la plus courante consiste à déposer un fichier dans / var / run / appelé [application] .pid, qui ne contient que le PID du processus en cours d'exécution, ou processus parent. Vous pouvez également créer un canal nommé dans le même répertoire pour pouvoir envoyer des messages au processus actif, par exemple. pour ouvrir un nouveau fichier.

J'ai créé un cadre de base pour l'exécution de ce type d'applications lorsque vous souhaitez pouvoir transmettre les arguments de ligne de commande des tentatives d'instances suivantes à la première. Une instance commencera à écouter sur un port prédéfini si elle ne trouve pas une instance qui y écoute déjà. Si une instance existe déjà, il envoie ses arguments de ligne de commande sur le socket et se ferme.

code avec explication

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top