确保Linux中应用程序的单实例
- 2024-10-21 09:14:00
- admin 原创
- 119
问题描述:
我正在使用 WxPython 开发 GUI 应用程序,但我不知道如何确保在机器上任何给定时间只有一个应用程序副本在运行。由于应用程序的性质,多次运行没有任何意义,并且会很快失败。在 Win32 下,我可以简单地创建一个命名互斥体并在启动时检查它。不幸的是,我不知道 Linux 中有什么工具可以做到这一点。
我正在寻找一种可以在应用程序意外崩溃时自动释放的功能。我不想因为应用程序崩溃而让用户不得不手动删除锁定文件。
解决方案 1:
正确的做法是使用咨询锁定;在 Python 中,这可以在模块flock(LOCK_EX)
中找到。fcntl
与 pidfile 不同,当您的进程由于任何原因死亡时,这些锁总是会自动释放,不存在与文件删除相关的竞争条件(因为不需要删除文件来释放锁),并且没有机会让其他进程继承 PID,从而看起来验证了陈旧的锁。
如果您想要非正常关机检测,您可以在获取锁之后将标记(例如,对于传统主义者来说,是您的 PID)写入文件,然后在正常关机之前(在持有锁的情况下)将文件截断为 0 字节状态;因此,如果没有持有锁并且文件非空,则表明发生了非正常关机。
解决方案 2:
使用模块完成锁定解决方案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)
解决方案 3:
有几种常见的技术,包括使用信号量。我看到最常用的是在启动时创建一个包含正在运行的进程的 pid 的“pid 锁定文件”。如果程序启动时该文件已经存在,则打开它并获取其中的 pid,检查是否有具有该 pid 的进程正在运行,如果是,则检查 /proc/ pid中的 cmdline 值以查看它是否是程序的实例,如果是,则退出,否则用您的 pid 覆盖该文件。pid 文件的常用名称是application_name.pid
。
解决方案 4:
wxWidgets 为此提供了一个 wxSingleInstanceChecker 类:wxPython doc或wxWidgets doc。wxWidgets doc 中有 C++ 示例代码,但 python 等效代码应如下所示(未经测试):
name = "MyApp-%s" % wx.GetUserId()
checker = wx.SingleInstanceChecker(name)
if checker.IsAnotherRunning():
return False
解决方案 5:
这建立在用户zgoda的回答之上。它主要解决了与锁定文件的写访问有关的棘手问题。特别是,如果锁定文件首先由 创建,则由于用户 缺乏写权限,另一个用户将无法成功重写此文件。显而易见的解决方案似乎是为每个人创建具有写权限的文件。此解决方案还建立在我的另一个答案之上,该答案与创建具有此类自定义权限的文件有关。这个问题在现实世界中很重要,因为您的程序可能由包括 在内的任何用户运行。root
`foofoo
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
权限。您可以自行决定使用哪个目录。
请注意,不需要打开锁定文件的文件句柄。
解决方案 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:
查找与 unix 上的 SYSV 信号量接口的 Python 模块。信号量具有 SEM_UNDO 标志,如果进程崩溃,它将导致释放进程所持有的资源。
否则,正如 Bernard 建议的那样,你可以使用
import os
os.getpid()
并将其写入 /var/run/ application_name .pid。进程启动时,应该检查 /var/run/ application_name .pid 中的 pid 是否在 ps 表中,如果是则退出,否则将自己的 pid 写入 /var/run/ application_name .pid。下面的 var_run_pid 就是你从 /var/run/ application_name .pid中读取的 pid
cmd = "ps -p %s -o comm=" % var_run_pid
app_name = os.popen(cmd).read().strip()
if len(app_name) > 0:
Already running
解决方案 8:
semaphore.h
我认为,在-- sem_open()
、等 --中定义的函数集sem_trywait()
是 POSIX 等效的。
解决方案 9:
如果您创建一个锁文件并将 pid 放入其中,您可以根据它检查您的进程 id 并判断是否崩溃,不是吗?
我个人没有这样做过,所以请酌情考虑。:p
解决方案 10:
您可以使用“pidof”实用程序吗?如果您的应用程序正在运行,pidof 会将应用程序的进程 ID 写入标准输出。如果没有,它将打印换行符 (LF) 并返回错误代码。
示例(来自 bash,为简单起见):
linux# pidof myapp
8947
linux# pidof nonexistent_app
linux#
解决方案 11:
目前最常见的方法是将一个名为 [application].pid 的文件放入 /var/run/,该文件仅包含正在运行的进程或父进程的 PID。作为替代方案,您可以在同一目录中创建一个命名管道,以便能够向活动进程发送消息,例如打开一个新文件。
解决方案 12:
当您希望能够将后续尝试实例的命令行参数传递给第一个实例时,我已创建了一个用于运行此类应用程序的基本框架。如果实例未找到已在预定义端口上监听的实例,它将开始监听该端口。如果实例已存在,它将通过套接字发送其命令行参数并退出。
带解释的代码
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件