如何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 終止後的狀態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=。