![systemd 如何处理托管进程子进程的死亡?](https://linux22.com/image/95234/systemd%20%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E6%89%98%E7%AE%A1%E8%BF%9B%E7%A8%8B%E5%AD%90%E8%BF%9B%E7%A8%8B%E7%9A%84%E6%AD%BB%E4%BA%A1%EF%BC%9F.png)
如何systemd
处理托管进程的子进程的死亡?
假设systemd
启动守护进程foo
,然后守护进程启动其他三个守护进程:bar1
、bar2
和bar3
。如果意外终止,会systemd
采取什么措施吗?根据我的理解,如果您没有通过更改属性来告知,Solaris 上的服务管理工具 (SMF)将被终止或重新启动。行为有什么不同吗?foo
bar2
foo
startd
ignore_error
systemd
编辑#1:
我编写了一个测试守护进程来测试systemd
的行为。调用该守护进程是mother_daemon
因为它会生成子进程。
#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
using namespace std;
int main(int argc, char* argv[])
{
cout << "Hi! I'm going to fork and make 5 child processes!" << endl;
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid > 0)
{
cout << "I'm the parent process, and i = " << i << endl;
}
if (pid == 0)
{
// The following four lines rename the process to make it easier to keep track of with ps
int argv0size = strlen(argv[0]);
string childThreadName = "mother_daemon child thread PID: ";
childThreadName.append( to_string(::getpid()) );
strncpy(argv[0],childThreadName.c_str(),argv0size + 25);
cout << "I'm a child process, and i = " << i << endl;
pause();
// I don't want each child process spawning its own process
break;
}
}
pause();
return 0;
}
这是由一个systemd
名为的单元控制的mother_daemon.service
:
[Unit]
Description=Testing how systemd handles the death of the children of a managed process
StopWhenUnneeded=true
[Service]
ExecStart=/home/my_user/test_program/mother_daemon
Restart=always
该mother_daemon.service
装置由以下装置控制mother_daemon.target
:
[Unit]
Description=A target that wants mother_daemon.service
Wants=mother_daemon.service
当我运行sudo systemctl start mother_daemon.target
(之后sudo systemctl daemon-reload
)时,我可以看到父守护进程和五个子守护进程。
杀死其中一个子项对父项没有影响,但杀死父项(从而触发重新启动)确实会重新启动子项。
停止也结束mother_daemon.target
了sudo systemctl stop mother_daemon.target
孩子们。
我认为这回答了我的问题。
答案1
事实并非如此。
主进程以正常方式处理其子进程的死亡。
这就是 POSIX 世界。如果进程A分叉了B,并且进程B分叉了C、D和E;那么进程 B 会看到C、D 和 E 终止后的状态。进程 A 不知道 C、D 和 E 发生了什么,这与 systemd 无关SIGCHLD
。wait()
为了让 A 知道 C、D 和 E 终止,必须发生两件事。
- A 必须将自己注册为“subreaper”。 systemd 就是这样做的,其他各种服务管理器(包括 upstart 和 nosh )也是如此
service-manager
。 - B 必须
exit()
.服务愚蠢地、错误地、徒劳地尝试“恶魔化”自己来做到这一点。
kevent()
(人们可以在 BSD 上变得聪明。但这是一个 Linux 问题。)
答案2
systemd
有主进程的概念。在 systemd 文档中,这被称为“主服务进程”或简称为“主进程”。
示例 4 中systemd.service 文档介绍了计算时的主要流程Type=forking
。
Restart=
systemd.service 文档中的文档描述与主进程相关的服务启动时的不同可能性。
以下是上面链接的“示例 4”中的关键文本:
systemd 会认为服务正在初始化,而原始程序仍在运行。一旦成功退出并且至少有一个进程保留(并且RemainAfterExit = no),则该服务被视为已启动。
通常,传统守护进程仅包含一个进程。因此,如果原进程终止后只剩下一个进程,systemd 就会认为该进程是服务的主进程。在这种情况下,$MAINPID 变量将在 ExecReload=、ExecStop= 等中可用。
如果存在多个进程,systemd 将无法确定主进程,因此它不会假设存在一个进程。在这种情况下,$MAINPID 将不会扩展为任何内容。然而,如果进程决定写入传统的 PID 文件,systemd 将能够从那里读取主 PID。请相应地设置 PIDFile=。