Убедитесь, что один экземпляр приложения в Linux - PullRequest
32 голосов
/ 21 октября 2008

Я работаю над приложением с графическим интерфейсом в WxPython, и я не уверен, каким образом я могу гарантировать, что в любой момент времени на компьютере работает только одна копия моего приложения. Из-за характера приложения запуск более одного раза не имеет никакого смысла и быстро завершится неудачей. Под Win32 я могу просто сделать именованный мьютекс и проверить это при запуске. К сожалению, я не знаю каких-либо средств в Linux, которые могли бы сделать это.

Я ищу что-то, что будет автоматически выпущено в случае неожиданного сбоя приложения. Я не хочу обременять своих пользователей необходимостью вручную удалять файлы блокировки, потому что я разбился.

Ответы [ 12 ]

60 голосов
/ 21 октября 2008

Правильная вещь - рекомендательная блокировка с использованием flock(LOCK_EX); в Python это находится в модуле fcntl .

В отличие от pid-файлов, эти блокировки всегда автоматически снимаются, когда ваш процесс по какой-либо причине умирает, не существует никаких условий гонки, связанных с удалением файла (так как файлу не нужно удалять , чтобы снять блокировку ), и нет никакого другого шанса, что другой процесс унаследует PID и, следовательно, появится для проверки устаревшей блокировки.

Если вы хотите обнаружить нечистое завершение работы, вы можете записать маркер (например, ваш PID для традиционалистов) в файл после захвата блокировки, а затем усечь файл до 0-байтового состояния перед чистым завершением (пока блокировка блокируется). проводится); таким образом, если блокировка не удерживается и файл не пуст, указывается нечистое завершение работы.

28 голосов
/ 21 октября 2008

Полное решение для блокировки с использованием модуля 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)
25 голосов
/ 21 октября 2008

Существует несколько распространенных методов, в том числе использование семафоров. Чаще всего я вижу, что при запуске создается «файл блокировки pid», который содержит pid запущенного процесса. Если файл уже существует при запуске программы, откройте его и возьмите pid внутри, проверьте, запущен ли процесс с этим pid, если он проверен, значение cmdline в / proc / pid чтобы увидеть, является ли это экземпляром вашей программы, если он затем завершается, в противном случае перезапишите файл с вашим pid. Обычное имя для файла pid: application_name .pid.

8 голосов
/ 24 января 2009

wxWidgets предлагает класс wxSingleInstanceChecker для этой цели: wxPython doc или wxWidgets doc . В документе wxWidgets есть пример кода на C ++, но эквивалент Python должен быть примерно таким (непроверенным):

  name = "MyApp-%s" % wx.GetUserId()
  checker = wx.SingleInstanceChecker(name)
  if checker.IsAnotherRunning():
      return False
6 голосов
/ 22 февраля 2013

Это основано на ответе пользователем zgoda . В основном это касается сложной проблемы, связанной с доступом на запись в файл блокировки. В частности, если файл блокировки был впервые создан root, другой пользователь foo не сможет успешно перезаписать этот файл из-за отсутствия разрешений на запись для пользователя foo. Кажется, очевидным решением является создание файла с правами на запись для всех. Это решение также основано на другом ответе , сделанном мной, для создания файла с такими пользовательскими разрешениями. Эта проблема важна в реальном мире, где ваша программа может запускаться любым пользователем, включая 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)

Ограничением вышеприведенного кода является то, что если файл блокировки уже существовал с неожиданными разрешениями, эти разрешения не будут исправлены.

Я бы хотел использовать /var/run/<appname>/ в качестве каталога для файла блокировки, но для создания этого каталога требуются разрешения root. Вы можете сами решить, какой каталог использовать.

Обратите внимание, что нет необходимости открывать дескриптор файла для файла блокировки.

4 голосов
/ 07 января 2016

Вот решение на основе 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)
1 голос
/ 21 октября 2008

Найдите модуль python, который взаимодействует с семафорами SYSV в Unix. Семафоры имеют флаг SEM_UNDO, который приведет к освобождению ресурсов, удерживаемых процессом, в случае сбоя процесса.

В противном случае, как предложил Бернард, вы можете использовать

import os
os.getpid()

И запишите его в /var/run/application_name.pid. Когда процесс запускается, он должен проверить, указан ли pid в /var/run/application_name.pid в таблице ps, и выйти, если он есть, в противном случае записать свой собственный pid в / var / run / application_name .pid. В следующем примере var_run_pid - это pid, который вы прочитали из /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
1 голос
/ 21 октября 2008

Набор функций, определенных в semaphore.h - sem_open(), sem_trywait() и т. Д., - я полагаю, эквивалент POSIX.

0 голосов
/ 18 февраля 2009

Я создал базовую среду для запуска приложений такого типа, когда вы хотите иметь возможность передавать аргументы командной строки последующих попыток экземпляров в первый. Экземпляр начнет прослушивание на заранее определенном порту, если он не найдет экземпляр, уже прослушивающий его. Если экземпляр уже существует, он отправляет свои аргументы командной строки через сокет и завершает работу.

код с объяснением

0 голосов
/ 21 октября 2008

На сегодняшний день наиболее распространенным методом является удаление файла в / var / run / с именем [application] .pid, который содержит только PID запущенного процесса или родительского процесса. В качестве альтернативы вы можете создать именованный канал в том же каталоге, чтобы иметь возможность отправлять сообщения активному процессу, например, чтобы открыть новый файл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...