Убедитесь, что один экземпляр приложения в Linux

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

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

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

Полное решение блокировки с помощью модуля 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)

Ответ 4

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

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

Ответ 5

Это основывается на ответе пользователем 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 /questions/66509/write-file-with-specific-permissions-in-python/454128#454128
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. Вы можете сами принять решение о том, какой каталог использовать.

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

Ответ 6

Здесь 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)

Ответ 8

Найдите модуль 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

Ответ 9

Если вы создадите файл блокировки и поместите pid в него, вы можете проверить свой идентификатор процесса на него и сообщить, если вы разбились, нет?

Я не делал этого лично, поэтому принимаю с достаточным количеством соли.: Р

Ответ 10

Можете ли вы использовать утилиту pidof? Если ваше приложение запущено, pidof будет записывать идентификатор процесса вашего приложения в стандартный вывод. Если нет, он напечатает новую строку (LF) и вернет код ошибки.

Пример (из bash, для простоты):

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#

Ответ 11

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

Ответ 12

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

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