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

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

我正在寻找应用程序意外崩溃时会自动发布的东西。 我不想让我的用户不得不手动删除locking文件,因为我崩溃了。

Solutions Collecting From Web of "确保Linux中的应用程序的单个实例"

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

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

与pidfiles不同的是,当你的进程因任何原因死亡时,这些锁总是自动释放的,没有与文件删除有关的竞争条件(因为不需要删除文件来释放锁),并且没有任何不同的机会进程继承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 doc或wxWidgets doc 。 wxWidgets文档在C ++中有示例代码,但python等价物应该是这样的(未经测试):

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

这建立在用户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, ie 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权限。 你可以自己决定使用哪个目录。

请注意,不需要打开锁文件的文件句柄。

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

寻找一个python模块,连接到unix上的SYSV信号量。 信号量有一个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.hsem_open()sem_trywait()等中定义的一组函数是POSIX的等价物。

如果你创建了一个锁文件并把它放在里面,你可以检查你的进程ID,并告诉你是否崩溃了,不是?

我没有亲自做过,所以拿适量的盐。 :p

你可以使用“pidof”工具吗? 如果您的应用程序正在运行,则pidof会将您的应用程序的进程ID写入标准输出。 如果没有,它将打印一个换行符(LF)并返回一个错误代码。

例子(为了简单起见,bash):

 linux# pidof myapp 8947 linux# pidof nonexistent_app linux# 

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

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

代码w /说明