【问题标题】:Killing a daemon using a PID file使用 PID 文件杀死守护进程
【发布时间】:2014-08-30 23:44:36
【问题描述】:

在运行守护程序时,一个常见的 Linux/UNIX 习惯用法是生成守护程序,并创建一个仅包含守护程序进程 ID 的 PID 文件。这样,要停止/重新启动守护程序,您只需拥有 kill $(cat mydaemon.pid)

的脚本

现在,这里有很多机会出现不一致的状态。假设运行守护程序的机器被强制关闭,然后重新启动。现在你有一个 PID 文件,它引用了一个不存在的进程。

好的,所以没问题...您的守护进程将尝试杀死不存在的进程,发现它不是真正的进程,然后照常继续。

但是...如果它一个真正的进程——只是不是你的守护进程怎么办?如果是别人的流程,或者其他重要的流程怎么办?你无法知道——所以杀死它是有潜在危险的。一种可能性是检查进程的名称。当然,这也不是万无一失的,因为没有理由另一个进程可能没有相同的名称。特别是,例如,如果您的守护进程在解释器(如 Python)下运行,在这种情况下,进程名称永远不会是唯一的 - 它只是“python”,在这种情况下,您可能会无意中杀死其他人的进程。

那么我们如何处理这种需要重启守护进程的情况呢?怎么知道pid文件中的PID一定是daemon?

【问题讨论】:

  • 通常你会向它发送一个 SIGTERM,它与 kill 相同,我相信没有任何标志
  • 您从哪个角度提出问题:从想要管理守护进程的系统程序员的角度来看,或者从想要提供脚本的守护进程开发人员的角度来看用户启动/停止她自己的守护进程?
  • 这是一个有趣的问题...我是从守护程序开发人员的角度思考问题的,但我不明白为什么这很重要。
  • 守护进程管理器(例如 systemd)通常一直运行并且是 PID 1。因此它完全了解所有守护进程(因为它们是 PID 1 的直接子进程,因为它们已经分叉并且分离),这使它处于稍微不同的位置。
  • 现实情况是,根本不存在向非子进程发送信号的无竞争方式。如果你觉得你绝对需要一种方法来杀死一个无竞争的管理器中的守护进程,你应该以不同的方式管理你的守护进程。 (通过 Unix 套接字通信关闭,使用 systemd 或其他 init 守护进程,或创建您自己的管理进程来生成和终止守护进程。)

标签: linux unix daemon pid


【解决方案1】:

如果您真的想为您的用户提供脚本,您可以让守护进程自行管理其 pidfile,并添加一个 atexit 和一个 SIGABRT 处理程序以取消链接 pidfile,即使在不正常关闭时也是如此。

更多方法还包括将进程启动时间存储在 pidfile 中。与易失性存储(例如/var/run)一起,这是识别进程的一种非常可靠的方法。不过,这使得 kill 命令更加复杂。

但是,我个人认为守护程序开发人员不应该(过多地)关心这一点,而让目标平台以管理守护程序的方式来处理它(systemd、upstart、good ol' SysV init 脚本)。这些通常有更多的知识:例如 systemd 会很乐意接受一个根本不分叉的守护进程,允许它直接监视其状态而无需 PID 文件。然后,您可以为最常见的解决方案提供配置文件(目前可能是 systemd,因为 Debian 也迁移到它,因此它也将很快进入 ubuntu),这通常比完整的守护进程管理更容易编写。

【讨论】:

  • 我认为甚至有一些与安全相关的机制使用(startup-time, pid)-tuple 来安全地识别进程。由于我无法找到这方面的来源,我更喜欢在评论中对此进行说明。也许有人知道 linux 在哪种机制中做到这一点?
【解决方案2】:

你只是不断增加偏执狂:

  • pid 文件
  • 进程名匹配
  • 一些沟通渠道/金丝雀

为了确保 pid 在重新启动后不会过时,您可以做的最重要的事情是将其存储在 /var/run,这是保证每次重新启动都会清除的位置。

对于进程名匹配,你实际上可以在fork/exec点重新定义进程名,这将允许你使用一些唯一的名字。

沟通渠道/金丝雀稍微复杂一些,容易出现一些问题。如果一个守护进程创建了一个命名套接字,那么套接字的存在+与守护进程连接和通信的能力将被视为进程正在运行的证据。

【讨论】: