【问题标题】:How to check if another instance of the app/binary is already running如何检查应用程序/二进制文件的另一个实例是否已在运行
【发布时间】:2012-06-04 13:41:42
【问题描述】:

我正在使用 Objective-c 在 Mac 中编写一个命令行应用程序

在应用程序启动时,我想检查同一应用程序的另一个实例是否已经在运行。如果是,那么我应该等待它完成或退出当前实例或退出另一个实例等。

有什么办法吗?

【问题讨论】:

    标签: objective-c macos osx-lion osx-snow-leopard command-line-tool


    【解决方案1】:

    对此的标准 Unix 解决方案是创建一个“运行文件”。当您启动时,您尝试创建该文件并将您的 pid 写入它(如果它不存在);如果确实存在,请从中读取 pid,如果有一个正在运行的程序具有该 pid 和您的进程名称,请等待/退出/随便什么。

    问题是,你把那个文件放在哪里,你怎么称呼它?

    首先,您必须确定“已经运行”的确切含义。显然不是“世界上的任何地方”,但它可以是从“当前机器上的任何地方”到“当前桌面会话中”的任何东西。 (例如,如果用户 A 启动您的程序,将其暂停,然后用户 B 出现并通过快速用户切换接管计算机,她是否能够运行该程序?)

    对于这个问题的任何合理答案,都有一个明显的路径名模式。例如,在 Mac 上,/tmp 在系统范围内共享,而 $TMPDIR 特定于给定会话,因此 /tmp/${ARGV[0]}.pid 是表示“只有一个在机器上复制,句号”,而 ${TMPDIR}/${ARGV[0]}.pid 是说“每个会话只有一个副本”的好方法。

    【讨论】:

    • 谢谢,我已经以类似的方式做到了这一点,我在想会有更好/直接的方式。我将在下面解释我是如何做到的。 “当前机器上的任何地方”是我需要的。
    • @abarnert 你没有竞争条件吗?在正常情况下不太可能发生,但如果一个进程频繁/自动启动,出现竞态条件的概率会更高。
    • @ring0:是的,一个简单的实现有一个竞争条件,但是有多种方法可以解决这个问题。基本上,它们都涉及乐观地做事并在意外失败时重试循环。例如,您可以在修改权限的同时执行 atomic-write-temp-and-rename,因此如果两个进程都尝试,其中一个进程将失败并重试。或者您可以就地创建和写入文件;一个进程可能看到现有文件但看不到任何内容,在这种情况下它必须重试。以此类推。
    • 最常见的解决方案没有解决的一个问题是,一个进程可能会死掉,留下它的 pidfile,然后启动两个新副本,其中一个具有相同的pid 作为被遗弃的。您可以通过编写 pid 加上启动时间戳来解决这个问题,但是您需要一种方法来获取另一个进程的启动时间,这很难跨平台......
    • 如果您这样做,请不要乱用 PID。只需使用flock 获取文件的排他锁。只有一个进程可以获取锁。如果锁失败,另一个进程拥有它。当该进程终止时,锁被释放。
    【解决方案2】:

    执行此操作的简单但常见的方法是检查进程列表以获取可执行文件的名称。

    ps - A | grep <your executable name>

    【讨论】:

      【解决方案3】:

      谢谢@abarnert。

      这就是我目前实施的方式。在 main() 开始时,我会检查二进制文件自己的目录中是否存在名为 .lock 的文件(我正在考虑将其移动到 /tmp)。如果是,则应用程序退出。

      如果没有,它将创建文件。 在应用程序结束时,.lock 文件被删除

      我还没有将 pid 写入该文件,但在需要退出前一个实例时我会这样做(目前我还不需要它,但将来可能会用到)。 我认为可以使用 PID 检索

      int myPID=[[NSProcessInfo processInfo] processIdentifier];
      

      该程序将由作为根守护程序运行的自定义调度程序调用。所以它将以root身份运行。

      看到答案,我会假设没有解决问题的直接方法。

      【讨论】:

      • 写pid有两个原因。首先,正如您所暗示的,这就是您可以杀死或以其他方式发出已经运行的副本的方式(您在原始问题中要求的选项之一)。其次,它可以让您将崩溃进程留下的废弃文件与活动文件区分开来(例如,通过使用信号 0 进行终止)。
      • 对,没有直接的方法可以做到这一点。间接方法有很多种,但它们都归结为两件事:某种可预测命名的事物(pidfile、socket、命名管道、NT 命名的内核互斥体),或某种广播机制(Mac 分布式通知、 Windows WM_ 广播消息等)。有一些库包含了详细信息(例如,请参阅 stackoverflow.com/questions/380870/… 了解一些 Python 解决方案),尽管我不知道的唯一 C 和 ObjC 是更大框架的一部分。
      • 我仍然对“锁定文件”概念有疑问。现在我正在考虑使用 ps -ef | grep | grep -vE 'grep' | awk '{print $2}' ,然后捕获输出并检查行数以了解实例数及其 pid。在这种情况下,我也可以通过检查 pid!=myPID 来终止其他实例。我将不得不检查如何通过 NSTask 发出命令并使用 NSPipe 捕获它。有什么建议吗?
      • 解析 ps 确实不是一个好的答案,原因有很多。您对“锁定文件”概念有什么问题?您可能想查看 linux 发行版中的 .rc 脚本,了解它们如何处理细节。
      • 我有一些由另一个调度程序二进制文件启动的二进制文件。一些二进制文件对不同的命令行参数有不同的功能。其中一些二进制文件可能由调度程序根据重复间隔同时执行。在这种情况下,锁文件概念随机失败。我检查是否存在锁定文件并在相邻行中创建锁定文件,但如果同时启动 4 或 5 个二进制实例,则 2 或 3 个通过锁定文件存在检查。哪个是.rc脚本,你指的是rc.d/init.d脚本还是bashrc?
      猜你喜欢
      • 1970-01-01
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多