我正在 WxPython 中开发 GUI 应用程序,我不确定如何确保在任何给定时间机器上只有应用程序的一个副本正在运行。由于应用程序的性质,多次运行没有任何意义,并且很快就会失败。在 Win32 下,我可以简单地创建一个命名互斥体并在启动时检查它。不幸的是,我不知道 Linux 中有任何工具可以做到这一点。

我正在寻找一些在应用程序意外崩溃时会自动释放的东西。我不想因为我崩溃而给我的用户带来必须手动删除锁定文件的负担。

有帮助吗?

解决方案

有几种常用技术,包括使用信号量。我经常看到的最常用的是创建一个<!>“pid锁定文件<!>”;在启动时包含正在运行的进程的pid。如果程序启动时该文件已存在,请将其打开并抓取内部的pid,检查是否正在运行具有该pid的进程,如果它正在检查/ proc / pid中的cmdline值查看它是否是您的程序的实例,如果它然后退出,否则用您的pid覆盖该文件。 pid文件的通常名称是 application_name .pid

其他提示

正确的做法是使用建议性锁定 flock(LOCK_EX);在 Python 中,可以在 fcntl 模块.

与 pidfile 不同,当您的进程因任何原因终止时,这些锁总是会自动释放,不存在与文件删除相关的竞争条件(因为文件不会 需要 被删除以释放锁),并且不同的进程不可能继承 PID,从而看起来验证了过时的锁。

如果你想要不干净的关闭检测,你​​可以在获取锁后将一个标记(例如你的PID,对于传统主义者)写入文件中,然后在干净关闭之前将文件截断为0字节状态(当锁被持有时) );因此,如果未持有锁并且文件非空,则表明不正常关闭。

使用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 为此提供了一个 wxSingleInstanceChecker 类: wxPython 文档, , 或者 wxWidgets 文档. 。wxWidgets 文档有 C++ 示例代码,但等效的 python 应该是这样的(未经测试):

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

这建立在用户答案的基础上 Zgoda酒店。它主要解决了与锁文件的写访问有关的棘手问题。特别是,如果锁定文件最初是由root创建的,则由于缺少用户foo的写入权限,另一个用户/var/run/<appname>/无法成功地再次尝试重写此文件。显而易见的解决方案似乎是为每个人创建具有写权限的文件。此解决方案还基于我的不同答案,必须创建具有此类自定义权限的文件。在现实世界中,这种担忧很重要,因为任何用户都可以运行您的程序,包括<=>。

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)

上述代码的限制是,如果锁定文件已存在且具有意外权限,则不会更正这些权限。

我希望使用<=>作为锁定文件的目录,但创建此目录需要<=>权限。您可以自己决定使用哪个目录。

请注意,无需打开锁定文件的文件句柄。

这是基于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)

查找一个与unix上的SYSV信号量接口的python模块。信号量具有SEM_UNDO标志,如果进程崩溃,将导致进程保留的资源被释放。

否则,正如伯纳德建议的那样,你可以使用

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

semaphore.h 中定义的一组功能 - sem_open()sem_trywait()等 - 我相信是POSIX的等价物。

如果您创建了一个锁定文件并将pid放入其中,您可以检查它的进程ID并告诉您是否崩溃了,不是吗?

我个人没有这样做,所以请加入适量的盐。 :P

你能使用'pidof'实用程序吗?如果您的应用正在运行,pidof会将您应用的进程ID写入stdout。如果没有,它将打印换行符(LF)并返回错误代码。

示例(来自bash,为简单起见):

linux# pidof myapp
8947
linux# pidof nonexistent_app

linux#

到目前为止,最常见的方法是将文件放入/ var / run / called [application] .pid,它只包含正在运行的进程的PID或父进程。 作为替代方案,您可以在同一目录中创建命名管道,以便能够将消息发送到活动进程,例如,打开一个新文件。

当您希望能够将后续尝试实例的命令行参数传递给第一个应用程序时,我已经为运行这些类型的应用程序创建了一个基本框架。如果实例未找到已在其中侦听的实例,则实例将开始侦听预定义端口。如果某个实例已经存在,它会通过套接字发送其命令行参数并退出。

代码w /解释

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top