Убедитесь, что один экземпляр приложения в 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)
Ответ 7
Набор функций, определенных в semaphore.h
- sem_open()
, sem_trywait()
и т.д. - эквивалент POSIX, Я верю.
Ответ 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
Я создал базовую структуру для запуска таких приложений, когда вы хотите передать аргументы командной строки последующих попыток экземпляров в первую. Экземпляр начнет прослушивать предопределенный порт, если он не найдет экземпляр, уже прослушивающий его. Если экземпляр уже существует, он отправляет аргументы командной строки через сокет и завершает работу.
код с объяснением