当前位置:  开发笔记 > 编程语言 > 正文

确保Linux中的单个应用程序实例

如何解决《确保Linux中的单个应用程序实例》经验,为你挑选了5个好方法。

我正在使用WxPython中的GUI应用程序,我不确定如何确保在计算机上的任何给定时间只运行我的应用程序的一个副本.由于应用程序的性质,运行不止一次没有任何意义,并且会很快失败.在Win32下,我可以简单地创建一个名为mutex并在启动时检查它.不幸的是,我不知道Linux中可以做到这一点的任何设施.

如果应用程序意外崩溃,我正在寻找会自动发布的内容.我不想让我的用户不得不手动删除锁定文件,因为我崩溃了.



1> Charles Duff..:

正确的事情是咨询锁定使用flock(LOCK_EX); 在Python中,这可以在fcntl模块中找到.

与pidfiles不同,当你的进程因任何原因死亡时,这些锁总是自动释放,没有与文件删除相关的竞争条件(因为文件不需要被删除以释放锁),并且没有可能出现不同的情况进程继承PID,从而出现验证过时的锁.

如果你想要不清洁的关机检测,你可以在获取锁之后将标记(例如你的PID,传统主义者)写入文件,然后在干净关闭之前将文件截断为0字节状态(同时保持锁定); 因此,如果未保持锁定且文件非空,则指示不正常关闭.


被删除以释放锁),并且没有可能出现不同的情况进程继承PID,从而出现验证过时的锁.
@jedierikb只有文件描述符可以保留POSIX顾问锁.是的,它们在硬关机等时正确释放.

2> zgoda..:

使用该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> Robert Gambl..:

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


这似乎有竞争条件?首先,假设我的程序实例死了,留下过时的PID文件.然后我同时启动了我的程序的两个新实例.这两个新实例可能都认为PID文件是陈旧的,然后一个接一个地覆盖PID文件.对于传统的UNIX守护程序来说,这不是问题,它们仅在引导时启动,并且由显式管理员命令行选择启动.但它可能是其他程序的问题.
按照惯例,这应该在/ var/run /下,正确吗?
@Kaz我认为这只是将比赛移至删除。假设:进程A创建了文件,然后崩溃了,将文件留在了后面。然后,进程B和C启动,并且都检查文件并看到文件中包含过时的PID,因此它们都决定删除并重新创建。进程B删除文件并自动重新创建。然后,进程C删除文件并自动重新创建它。进程B和C都认为它们具有互斥锁。从根本上讲,在没有内核支持的情况下处理陈旧的锁非常困难-这就是为什么您应该改用flock()的原因。

4> Brian Victor..:

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> Acumenus..:

这建立在用户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 /sf/ask/17360801/
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//其用作锁定文件的目录,但创建此目录需要root权限.您可以自己决定使用哪个目录.

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

推荐阅读
yzh148448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有