Domanda

Sto lavorando su un'applicazione GUI in WxPython, e io non sono sicuro di come posso avere solo una copia della mia applicazione è in esecuzione in qualsiasi momento sulla macchina.A causa della natura dell'applicazione, l'esecuzione di più di una volta, non ha alcun senso, e non in fretta.Sotto Win32, posso semplicemente fare un mutex denominato e controllare che all'avvio.Purtroppo, non so di eventuali strutture in Linux che può fare questo.

Sto cercando qualcosa che verrà automaticamente rilasciato qualora il crash dell'applicazione in modo imprevisto.Io non voglio essere di peso ai miei utenti con la necessità di eliminare manualmente i file lock, perché sono caduto.

È stato utile?

Soluzione

Esistono diverse tecniche comuni, incluso l'uso dei semafori. Quello che vedo usato più spesso è creare un & Quot; file pid lock & Quot; all'avvio che contiene il pid del processo in esecuzione. Se il file esiste già all'avvio del programma, aprilo e prendi il pid all'interno, controlla se è in esecuzione un processo con quel pid, se controlla il valore della cmdline in / proc / pid per vedere se si tratta di un'istanza del tuo programma, se viene quindi chiuso, altrimenti sovrascrivi il file con il tuo pid. Il solito nome per il file pid è nome_applicazione .pid.

Altri suggerimenti

La Cosa Giusta è consultivo blocco utilizzando flock(LOCK_EX);in Python, questo si trova nel fcntl modulo.

A differenza di pidfile, questi blocchi sono sempre rilasciato automaticamente quando il processo muore per qualsiasi motivo, non hanno razza condizioni relative alla cancellazione del file (il file non bisogno per essere eliminati per rilasciare il blocco), e non c'è possibilità di un diverso processo di ereditare il PID e quindi appare per convalidare un vecchio blocco.

Se desideri impuri arresto di rilevamento, è possibile scrivere un marcatore (come il PID, per i tradizionalisti) nel file dopo la cattura il blocco, e poi troncare il file di 0 byte di stato prima di un arresto pulito (mentre il blocco è in corso);così, se il blocco non è tenuto e il file non è vuota, è un impuro l'arresto è indicato.

Soluzione di blocco completa utilizzando il modulo 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 offre una classe wxSingleInstanceChecker per questo scopo: wxPython doc o wxWidgets doc . Il documento wxWidgets ha un codice di esempio in C ++, ma l'equivalente di Python dovrebbe essere qualcosa del genere (non testato):

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

Questo si basa sulla risposta dell'utente Zgoda . Risolve principalmente una problematica problematica legata all'accesso in scrittura al file di blocco. In particolare, se il file di blocco è stato creato per la prima volta da root, un altro utente foo non può più tentare con successo di riscrivere questo file a causa dell'assenza di autorizzazioni di scrittura per l'utente /var/run/<appname>/. La soluzione ovvia sembra essere quella di creare il file con autorizzazioni di scrittura per tutti. Questa soluzione si basa anche su una diversa risposta da parte mia, dovendo creare un file con tali autorizzazioni personalizzate. Questa preoccupazione è importante nel mondo reale in cui il tuo programma può essere eseguito da qualsiasi utente incluso <=>.

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)

Una limitazione del codice sopra è che se il file di blocco esisteva già con autorizzazioni impreviste, tali autorizzazioni non verranno corrette.

Mi sarebbe piaciuto usare <=> come directory per il file di blocco, ma la creazione di questa directory richiede <=> autorizzazioni. Puoi decidere autonomamente quale directory utilizzare.

Nota che non è necessario aprire un handle di file nel file di blocco.

Ecco la soluzione basata sulla porta 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)

Cerca un modulo Python che si interfaccia ai semafori SYSV su unix. I semafori hanno un flag SEM_UNDO che causerà il rilascio delle risorse trattenute da un processo in caso di arresto anomalo del processo.

Altrimenti, come ha suggerito Bernard, puoi usare

import os
os.getpid()

E scrivilo su / var / run / nome_applicazione .pid. All'avvio del processo, dovrebbe verificare se il pid in /var/run/application_name.pid è elencato nella tabella ps e chiudere se lo è, altrimenti scrivere il proprio pid in / var / run / nome_applicazione .pid. Nel seguente var_run_pid è il pid che leggi da /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

L'insieme di funzioni definite in semaphore.h - sem_open(), sem_trywait(), ecc. - sono l'equivalente POSIX, credo.

Se si crea un file di blocco e si inserisce il pid in esso, è possibile verificare l'id del processo rispetto a esso e dire se si è verificato un arresto anomalo, no?

Non l'ho fatto personalmente, quindi prendi con adeguate quantità di sale. : P

Puoi usare l'utilità 'pidof'? Se la tua app è in esecuzione, pidof scriverà l'ID processo della tua app su stdout. In caso contrario, stamperà una nuova riga (LF) e restituirà un codice di errore.

Esempio (da bash, per semplicità):

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#

Di gran lunga il metodo più comune è quello di rilasciare un file in / var / run / chiamato [application] .pid che contiene solo il PID del processo in esecuzione o processo parent. In alternativa, è possibile creare una pipe denominata nella stessa directory per poter inviare messaggi al processo attivo, ad es. per aprire un nuovo file.

Ho creato un framework di base per eseguire questo tipo di applicazioni quando si desidera poter passare gli argomenti della riga di comando delle successive istanze tentate alla prima. Un'istanza inizierà l'ascolto su una porta predefinita se non trova un'istanza già in ascolto lì. Se esiste già un'istanza, invia gli argomenti della riga di comando sul socket ed esce.

codice con spiegazione

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top