Pergunta

Eu estou trabalhando em uma aplicação gráfica em wxPython, e eu não sei como eu posso garantir que apenas uma cópia do meu aplicativo está sendo executado em um determinado momento na máquina. Devido à natureza da aplicação, executando mais de uma vez, não faz qualquer sentido, e irá falhar rapidamente. Em Win32, eu posso simplesmente fazer um mutex nomeado e verifique se na inicialização. Infelizmente, eu não sei de quaisquer instalações em Linux que podem fazer isso.

Eu estou procurando algo que será automaticamente liberada se o pedido falhar inesperadamente. Eu não quero ter que fardo meus usuários com a necessidade de arquivos de bloqueio excluir manualmente porque caí.

Foi útil?

Solução

Existem várias técnicas comuns incluindo o uso de semáforos. O que eu vejo mais utilizado é o de criar um "arquivo de bloqueio pid" na inicialização que contém o pid do processo de execução. Se o arquivo já existe quando o programa é iniciado, abri-lo e pegar o interior pid, verificar para ver se um processo com que pid está em execução, se é verificar o valor cmdline em / proc / pid para ver se ele é uma instância do seu programa, se ele é, então, parar, caso contrário, substituir o arquivo com o seu pid. O nome usual para o arquivo pid é application_name .pid.

Outras dicas

A Coisa Certa é bloqueio consultivo usando flock(LOCK_EX); em Python, este encontra-se na fcntl módulo .

Ao contrário pidfiles, esses bloqueios são sempre liberados automaticamente quando o dies processo por qualquer razão, não têm condições de corrida existentes a exclusão do arquivo (como o arquivo não necessidade para ser apagado para liberar o lock), e não há nenhuma chance de um processo diferente herdar o PID e aparecendo, assim, para validar um bloqueio obsoleto.

Se você quiser detecção desligamento imundo, você pode escrever um marcador (como o PID, para os tradicionalistas) para o arquivo depois de agarrar a fechadura e truncar o arquivo para o status de 0 bytes antes de um desligamento normal (enquanto o bloqueio está sendo realizada); Assim, se o bloqueio não é realizada e o arquivo não é vazio, um desligamento imundo é indicado.

solução de bloqueio completo usando o 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 ofertas uma classe wxSingleInstanceChecker para esta finalidade: wxPython doc ou wxWidgets doc . O doc wxWidgets tem código de exemplo em C ++, mas o equivalente python deve ser algo parecido com isto (não testado):

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

Esta baseia-se na resposta pelo usuário Zgoda . Ele aborda principalmente uma preocupação complicado ter que fazer com acesso de gravação para o arquivo de bloqueio. Em particular, se o arquivo de bloqueio foi criado pela primeira vez por root, outro foo usuário pode então nenhum sucesso mais tentar reescrever este arquivo devido a uma ausência de permissões de gravação para foo usuário. A solução óbvia parece ser a de criar o arquivo com permissões de escrita para todos. Esta solução também se baseia em uma resposta diferente por mim, ter que fazer a criação de um arquivo com essas permissões personalizadas. Essa preocupação é importante no mundo real onde o programa pode ser executado por qualquer usuário, incluindo 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://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)

Uma limitação do código acima é que, se o arquivo de bloqueio já existia com permissões inesperados, essas permissões não serão corrigidos.

Eu teria gostado de usar /var/run/<appname>/ como o diretório para o arquivo de bloqueio, mas criar este diretório requer permissões root. Você pode fazer sua própria decisão para a qual diretório para uso.

Note que não há necessidade de abrir um identificador de arquivo para o arquivo de bloqueio.

Aqui está a solução baseada em 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)

Procure um módulo python que interfaces para semáforos SYSV no Unix. Os semáforos têm uma bandeira SEM_UNDO que fará com que os recursos mantidos pelo um processo para ser lançado se o processo deixa de funcionar.

Caso contrário, como Bernard sugeriu, você pode usar

import os
os.getpid()

e escrevê-lo para /var/run/application_name.pid. Quando o processo é iniciado, ele deve verificar se o pid em /var/run/application_name.pid está listado na tabela de ps e sair se for, caso contrário, escrever seu próprio pid em / var / run / application_name .pid. No seguinte var_run_pid é o PID você ler de / 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

O conjunto de funções definidas em semaphore.h - sem_open(), sem_trywait(), etc - são o equivalente POSIX, acredito

.

Se você criar um arquivo de bloqueio e colocar o pid nele, você pode verificar o seu ID do processo contra ele e dizer se você caiu, não?

Eu não fiz isso pessoalmente, assim que tomar com quantidades apropriadas de sal. : P

Você pode usar o utilitário 'pidof'? Se seu aplicativo está sendo executado, pidof vai escrever o ID do processo do seu aplicativo para stdout. Se não, ele irá imprimir uma nova linha (LF) e retornar um código de erro.

Exemplo (a partir de bater, para simplificar):

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#

De longe o método mais comum é a queda de um arquivo em / var / run / chamada [aplicação] .pid que contém apenas o PID do processo de execução, ou processo pai. Como alternativa, você pode criar um pipe nomeado no mesmo diretório para ser capaz de enviar mensagens para o processo ativo, por exemplo, para abrir um novo arquivo.

Eu fiz uma estrutura básica para executar estes tipos de aplicações quando você quer ser capaz de passar os argumentos de linha de comando de tentativas de casos posteriores à primeira. Uma instância vai começar a ouvir em uma porta predefinida se não encontrar uma instância já ouvindo lá. Se uma instância já existir, ele envia os seus argumentos de linha de comando sobre o soquete e saídas.

código w / explicação

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top