Как systemd
обрабатывается смерть потомков управляемых процессов?
Предположим, что systemd
запускается демон foo
, который затем запускает три других демона: bar1
, bar2
, и bar3
. Будет ли systemd
что-либо делать, foo
если bar2
неожиданно завершится? Насколько я понимаю, в Service Management Facility (SMF) на Solaris 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 — это то, что видит SIGCHLD
и wait()
статус завершения C, D и E. Процесс A не знает, что происходит с C, D и E, и это не зависит от systemd.
Для того чтобы А узнал о завершении C, D и E, должны произойти две вещи.
- А должензарегистрировать себя как "субреапер"Это делает systemd, а также различные другие менеджеры служб, включая upstart и nosh
service-manager
. - B должен
exit()
. Услуги, которыеглупо, ошибочно и тщетнопытаются «демонизировать» себя, делают это.
(С BSD можно помудрить kevent()
. Но это вопрос Linux.)
решение2
systemd
имеет концепцию основного процесса. В документации systemd это называется "основной процесс службы" или просто "основной процесс".
Пример 4 вsystemd.service документацияописывает основной процесс, который рассчитывается, когда Type=forking
.
Документация Restart=
в systemd.service docsописать различные возможности запуска службы по отношению к основному процессу.
Вот ключевой текст из «примера 4», ссылка на который приведена выше:
systemd будет считать, что служба находится в процессе инициализации, пока исходная программа все еще работает. После успешного завершения и при этом остается хотя бы один процесс (и RemainAfterExit=no), служба считается запущенной.
Часто традиционный демон состоит только из одного процесса. Поэтому, если после завершения исходного процесса остается только один процесс, systemd будет считать этот процесс основным процессом службы. В этом случае переменная $MAINPID будет доступна в ExecReload=, ExecStop= и т. д.
Если останется более одного процесса, systemd не сможет определить основной процесс, поэтому не будет предполагать, что он есть. В этом случае $MAINPID не будет расширяться ни во что. Однако, если процесс решит записать традиционный PID-файл, systemd сможет прочитать основной PID оттуда. Пожалуйста, установите PIDFile= соответствующим образом.