systemd 可以处理双叉守护进程吗?
我以前用过初始化.d/我编写的管理守护进程的脚本多年来一直完美运行。现在我发现自己需要提供一个系统/脚本,但我无法让它以我执行守护程序设置代码的方式工作。
当我设置一个守护进程时,我分叉两次以释放控制终端并避免成为会话领导者。
问题是我已经确认当你这样做时系统/失去追踪并杀死守护进程。我还确认跳过第二个叉子可以使其再次工作系统/。
我的基本(为了讨论的目的稍微简化)systemd/脚本是
[Unit]
Description=GM7 Service Daemon
[Service]
Type=forking
ExecStart=/usr/bin/g7ctrl
[Install]
WantedBy=multi-user.target
所以我想我的问题是:我是否需要改变过去十年在 C/C++ 中完成守护进程设置代码的方式,或者是否有选项让 systemd/ 来跟踪双分叉?
我还没有完全了解内部工作原理,如果 systemd 本身为它启动的每个守护进程生成进程,在这种情况下,我的双叉确实会过时
我承认我这样做的方式(双叉)部分是出于历史原因,但当时我对最佳实践进行了一些相当深入的阅读,这就是我后来想到的(大约十年前)
答案1
我是否需要改变过去十年在 C/C++ 中完成守护进程设置代码的方式?
是的。
即使在大约十年前,这也不是正确的方法。自 20 世纪 90 年代初以来,AIX 系统资源控制器就一直不太对劲。 daemontools 用户从 20 世纪 90 年代末开始就提倡不要这样做。它不适合 2006 年主流 Linux 操作系统开始采用的 Upstart。它不适合运行inittab
20 世纪 80 年代初 AT&T System 5 Release 3 的东西。
它不适合 systemd。尽管人们可能会告诉你这种forking
类型,但他们通常会忘记,甚至不知道告诉你,这是一种具体的服务准备协议,这取决于以特定方式完成的双分叉。更糟糕的是,正确表达协议所需的方式与您和其他人编写 C/C++ 程序的方式不能很好地融合。
早在 2008 年,我关于这个主题的常见答案已经存在了大约四分之三的时间,在我把它变成 FGA 形式之前,我和其他人已经告诉人们同样的事情很长一段时间了。
IBM 自 1995 年起就在红皮书中提到了这一点。
一些新来的人从 systemd 的角度来看,在 systemd 手册中说了同样的话,时间只有 8 年左右。 ☺
让服务管理子系统来处理这一切。你的程序是已经当它开始运行时在守护进程上下文中执行。
如果你真的想保留所有依赖于守护进程谬论的代码都是正确的,那么至少做许多其他人在过去二十年所做的事情(部分是为了回应 daemontools 的人),并给世界一个命令 -行选项将其全部关闭。但请不要让该命令行选项充当调试开关的双重职责。
进一步阅读
- 乔纳森·德博因·波拉德 (2001)。 设计 Unix 守护程序时要避免的错误。经常给出的答案。
- 乔纳森·德博因·波拉德 (2015)。您确实不需要守护进程。真的。。 systemd 恐怖屋。
- 乔纳森·德博因·波拉德 (2015)。Unix 守护进程的就绪协议问题。常见答案。
- https://unix.stackexchange.com/a/401611/5132
答案2
是的,systemd 在使用Type=forking
.
只要主进程退出时还剩下一个进程,systemd 就会认为它是主进程。
看这个关于传统分叉服务的例子在 systemd 文档中了解更多详细信息和更全面的工作原理描述Type=forking
。
systemd 对于初始启动进程的退出以及守护进程的启动和准备更加严格。从那个例子来看:
一旦成功退出并且至少有一个进程保留(和
RemainAfterExit=no
),该服务就被认为已启动。
因此,您的情况可能是启动进程在分叉第一个子进程(systemd 然后认为是您服务的主进程)后立即退出,然后第一个子进程再次分叉并退出,因此 systemd 认为服务已经完成,但实际上还没有。
有些人会争论即使在 System V init 上,这也是损坏的行为:
同样,不正确同步对 sysvinit 的影响与对 systemd 的影响一样大。我们在某种程度上会处理这个问题,但处理这个问题的唯一完全安全和正确的方法是修复有问题的程序。
无论如何,我给你的最好建议是在 systemd 下运行时跳过双重分叉。你不必消除守护进程的双重分叉能力,只需添加一个命令行选项(如-f
、-F
或--foreground
)即可跳过守护进程代码并在前台运行程序。在您的 systemd 服务文件中使用该选项。
(无论如何,当运行守护进程进行调试或故障排除时,这样的选项也会很有帮助。)
systemdType=forking
能够与双分叉守护进程一起工作,但最终,它的存在只是为了兼容性。不分叉有很多优点(更容易跟踪主进程),因此如果您能够修改程序,那么这样做是最好的。