Por que “o processo não deve bifurcar” para serviços de tipo simples no systemd?

Aug 15 2020

Quero escrever meus próprios systemdarquivos de unidade para gerenciar comandos 1 de execução realmente longa (na ordem de horas). Ao examinar o artigo do ArchWiki no systemd , ele diz o seguinte sobre a escolha de um tipo de inicialização:

Type=simple(padrão): o systemd considera o serviço a ser iniciado imediatamente. O processo não deve bifurcar . Não use este tipo se outros serviços precisarem ser solicitados neste serviço, a menos que seja ativado por soquete.

Por que o processo não deve bifurcar? Está se referindo a bifurcação no estilo do processo de invocação do daemon (bifurcações pai e, em seguida, saídas) ou qualquer tipo de bifurcação?


1 Não quero tmux / screen porque quero uma forma mais elegante de verificar o status e reiniciar o serviço sem precisar recorrer a tmux send-keys.

Respostas

41 Gilles'SO-stopbeingevil' Aug 15 2020 at 17:29

O serviço tem permissão para chamar a chamada do forksistema. O Systemd não o impedirá, nem mesmo perceberá se isso acontecer. Esta sentença se refere especificamente à prática de bifurcação no início de um daemon para isolar o daemon de seu processo pai. “O processo não deve bifurcar [e sair do pai enquanto executa o serviço em um processo filho]”.

A página do manual explica isso de forma mais detalhada e com uma formulação que não leva a essa confusão específica.

Muitos programas que devem ser usados ​​como daemons têm um modo (geralmente o modo padrão) em que, quando iniciam, eles se isolam de seus pais. O daemon é iniciado, chama fork()e o pai sai. O processo filho chama setsid()para que seja executado em seu próprio grupo de processos e sessão, e execute o serviço. O objetivo é que se o daemon for invocado a partir de uma linha de comando do shell, o daemon não receberá nenhum sinal do kernel ou do shell, mesmo se algo acontecer com o terminal, como o fechamento do terminal (neste caso, o shell envia SIGHUP a todos os grupos de processos que conhece). Isso também faz com que o processo de serviço seja adotado pelo init, que vai colher isso quando sair, evitando um zumbi se o daemon for iniciado por algo que não seria wait()para ele (isso não aconteceria se o daemon fosse iniciado por um shell )

Quando um daemon é iniciado por um processo de monitoramento como o systemd, a bifurcação é contraproducente. O processo de monitoramento deve reiniciar o serviço se ele travar, então ele precisa saber se o serviço é encerrado, e isso é difícil se o serviço não for filho direto do processo de monitoramento. O processo de monitoramento nunca deve morrer e não tem um terminal de controle, portanto, não há preocupações com sinais indesejados ou colheita. Portanto, não há razão para que o processo de serviço não seja filho do monitor e há uma boa razão para ser.

15 JdeBP Aug 16 2020 at 11:59

Ignore esta página do Arch wiki.

Ele tem coisas consideravelmente erradas no que diz respeito ao Typecenário. simpleAlém disso, isso não se limita apenas às descrições de . O que ele diz sobre forkingestá errado também.

As recomendações corretas para esse tipo de coisa existem há décadas a mais do que o próprio systemd, e remontam pelo menos ao início dos anos 1990. Como observei emhttps://unix.stackexchange.com/a/476608/5132, no doco do systemd há uma versão Johnny-come-ultimaly das recomendações para daemons que repete amplamente o que daemontools usuários, IBM, pessoas usando inittabe ... bem ... eu venho dizendo há décadas. (Já era uma resposta dada com frequência quando eu escrevi como tal em 2001).

Repetir:

Se o seu programa tem algum mecanismo de "desmonização", que em particular bifurca um filho e sai do processo pai, desligue-o e não o use . Graças a daemontools et al. onde isso tem sido um requisito por muito tempo, muitos programas desenvolveram a capacidade de não ter tais mecanismos nos últimos mais de 20 anos, e outros simplesmente não padronizam a "desmonização" em primeiro lugar, então podem ser usados ​​em seus modos de operação padrão.

Os subsistemas de gerenciamento de serviço já iniciam processos de serviço no contexto daemon . Esses processos não precisam "desmonizar". (Na verdade, é uma falácia em muitos sistemas operacionais modernos pensar que os programas podem até mesmo "desmonizar" a partir de um contexto de sessão de login, que é o que a "desmonização" realmente trata.) Eles já têm os valores de ambiente e descritores de arquivo abertos, apropriado ao contexto dos daemons, e as várias coisas feitas por "damonização" na verdade frustram algumas das coisas convencionais que são feitas regularmente com os daemons (por exemplo, capturar suas saídas / erros padrão em um log) pelos gerentes de serviço.

Prefira Type=simple, com abertura antecipada de soquete (onde o gerenciamento de serviço abre soquetes de servidor e os passa como descritores de arquivo já abertos para o programa de serviço), ou Type=notify.

  • Type=simple trata o serviço como pronto (de modo que os serviços solicitados possam ser iniciados / interrompidos) assim que o processo do serviço é iniciado, com a abertura de soquete antecipada empregando semântica de conexão de soquete para atrasar os clientes de serviço, nos pontos em que eles tentam se conectar aos servidores para serviço, até que os servidores estejam realmente prontos.
  • Type=notifytem a desvantagem de ser peculiar ao systemd e ao Linux (junto com os problemas de não ser funcional de processos de curta duração, como a geração de shell systemd-notify, e de empregar a análise de formas legíveis por humanos para formas legíveis por máquina em um processo privilegiado, onde problemas do analisador já ocorreram no passado), mas tem a vantagem de fornecer um controle mais preciso (do ponto de vista do programa de serviço) de quando o serviço é realmente considerado pronto. Também permite alguma personalização da saída de status.

Os programas de serviço, de ambos os tipos, podem ser bifurcados. O problema é a bifurcação e a saída do processo original .

(Deve-se notar que este é um problema tanto para executar programas a partir de shells quanto para executar programas de gerenciadores de serviço, com os usuários vendo os programas serem encerrados e causar outro prompt de shell quase imediatamente. Na verdade, hoje mesmo alguém estava perguntando, mais uma vez , sobre como executar programas a partir do shell que bifurcam e saem do pai, em Por que às vezes quando executo um programa no terminal, ele não é executado no terminal? )

Type=oneshotprovavelmente não é o que você deseja neste caso específico, pois o serviço é considerado pronto apenas quando todo o programa de serviço foi executado até a conclusão. Ele tem seus usos, mas pelo que parece eles não se aplicam a você.

Nunca use Type=forking. Deve ser o último recurso de desespero, já que quase nenhum programa realmente fala o protocolo . Eles estão fazendo outra coisa , que de fato não é este protocolo, não é interoperável corretamente com este protocolo e não sinaliza prontidão.

Leitura adicional

  • Jonathan de Boyne Pollard (2001). Erros a serem evitados ao projetar programas daemon do Unix . Respostas frequentemente fornecidas.
  • Jonathan de Boyne Pollard (2015). Você realmente não precisa daemonizar. Mesmo. . The systemd House of Horror.
  • Jonathan de Boyne Pollard (2015). Problemas de protocolo de prontidão com daemons Unix . Respostas frequentemente fornecidas.
  • https://unix.stackexchange.com/a/401611/5132