Perché "il processo non deve biforcare" per servizi di tipo semplice in systemd?

Aug 15 2020

Voglio scrivere i miei systemdfile di unità per gestire comandi di esecuzione molto lunghi 1 (nell'ordine di ore). Mentre si guarda l' articolo di ArchWiki su systemd , si dice quanto segue riguardo alla scelta di un tipo di avvio:

Type=simple(predefinito): systemd considera il servizio da avviare immediatamente. Il processo non deve biforcarsi . Non utilizzare questo tipo se è necessario ordinare altri servizi su questo servizio, a meno che non sia attivato tramite socket.

Perché il processo non deve assolutamente biforcarsi? Si riferisce al fork nello stile del processo di evocazione del demone (parent fork, quindi esce) o qualsiasi tipo di fork?


1 Non voglio tmux / screen perché voglio un modo più elegante di controllare lo stato e riavviare il servizio senza ricorrere a tmux send-keys.

Risposte

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

Il servizio è autorizzato a chiamare la forkchiamata di sistema. Systemd non lo impedirà, né noterà se lo fa. Questa frase si riferisce specificamente alla pratica del fork all'inizio di un daemon per isolare il daemon dal suo processo genitore. "Il processo non deve eseguire il fork [ed uscire dal genitore durante l'esecuzione del servizio in un processo figlio]".

La pagina man lo spiega in modo più verbale e con una formulazione che non porta a questa particolare confusione.

Molti programmi che sono pensati per essere usati come demoni hanno una modalità (spesso la modalità predefinita) in cui quando si avviano, si isolano dal loro genitore. Il demone si avvia, chiama fork()e il genitore esce. Il processo figlio chiama in setsid()modo che venga eseguito nel proprio gruppo di processi e sessione ed esegue il servizio. Lo scopo è che se il demone viene invocato da una riga di comando della shell, il demone non riceverà alcun segnale dal kernel o dalla shell anche se succede qualcosa al terminale come la chiusura del terminale (nel qual caso la shell invia SIGHUP a tutti i gruppi di processi di cui è a conoscenza). Questo fa sì che il processo di manutenzione venga adottato da init, che lo raccoglierà quando esce, evitando uno zombi se il demone fosse stato avviato da qualcosa che non lo avrebbe wait()fatto (questo non accadrebbe se il demone fosse stato avviato da una shell ).

Quando un daemon viene avviato da un processo di monitoraggio come systemd, il fork è controproducente. Il processo di monitoraggio dovrebbe riavviare il servizio se si arresta in modo anomalo, quindi deve sapere se il servizio viene chiuso e questo è difficile se il servizio non è un figlio diretto del processo di monitoraggio. Il processo di monitoraggio non dovrebbe mai morire e non ha un terminale di controllo, quindi non ci sono preoccupazioni in merito a segnali indesiderati o raccolta. Quindi non c'è motivo per cui il processo di servizio non sia figlio del monitor, e c'è una buona ragione perché lo sia.

15 JdeBP Aug 16 2020 at 11:59

Ignora questa pagina wiki di Arch.

Ha delle cose decisamente sbagliate rispetto Typeall'ambientazione. simpleInoltre, ciò non si limita solo alle sue descrizioni . Anche quello che dice forkingè sbagliato.

Le raccomandazioni corrette per questo genere di cose esistono da decenni più a lungo di quanto systemd stesso sia esistito e risalgono almeno all'inizio degli anni '90. Come ho notato ahttps://unix.stackexchange.com/a/476608/5132, nel documento systemd c'è una versione di Johnny-come-recentemente delle raccomandazioni per i demoni che ripete in gran parte ciò che gli utenti di daemontools, IBM, le persone che usano inittabe ... beh ... ho detto per decenni. (Era già una risposta data frequentemente quando l'ho scritta come tale nel 2001.)

Ripetere:

Se il programma ha qualche meccanismo "dæmonization", che in particolare le forcelle un bambino ed esce il processo padre, spegnerlo e non lo uso . Grazie a daemontools et al. dove questo è stato un requisito per molto tempo, molti programmi hanno sviluppato la capacità di non disporre di tali meccanismi negli ultimi 20 anni, e altri semplicemente non utilizzano per impostazione predefinita la "demonizzazione" in primo luogo, quindi possono essere utilizzati in le loro modalità operative predefinite.

I sottosistemi di gestione dei servizi avviano già i processi di servizio nel contesto del demone . Questi processi non devono essere "demonizzati". (In effetti, è un errore su molti sistemi operativi moderni pensare che i programmi possano persino "demonizzare" da un contesto di sessione di accesso, che è ciò di cui si tratta in realtà la "demonizzazione".) Hanno già i valori dell'ambiente e descrittori di file aperti, appropriato al contesto del demone, e le diverse cose fatte dalla "demonizzazione" di fatto contrastano alcune delle cose convenzionali che vengono fatte regolarmente con i demoni (ad esempio, catturare i loro output / errori standard in un registro) dai responsabili del servizio.

Preferisci Type=simple, con apertura anticipata del socket (dove la gestione del servizio apre i socket del server e li passa come descrittori di file già aperti al programma di servizio), o Type=notify.

  • Type=simple considera il servizio come pronto (in modo che i servizi ordinati su di esso possano essere avviati / arrestati) non appena il processo del servizio inizia, con l'apertura anticipata del socket che impiega la semantica della connessione socket per ritardare i client del servizio, nei punti in cui tentano di connettersi ai server per fino a quando i server non sono effettivamente pronti.
  • Type=notifyha lo svantaggio di essere peculiare di systemd e di Linux (oltre ai problemi di non essere funzionale a causa di processi di breve durata come lo spawning della shell systemd-notify, e di impiegare l'analisi di forme leggibili dall'uomo in forme leggibili dalla macchina in un processo privilegiato, dove problemi del parser si sono già verificati in passato) ma ha il vantaggio di fornire un controllo più preciso (dal punto di vista del programma di servizio) di quando il servizio è effettivamente considerato pronto. Consente inoltre alcune personalizzazioni dell'output di stato.

I programmi di servizio, di entrambi i tipi, possono eseguire il fork. È il fork e poi l'uscita dal processo originale che è il problema.

(Va notato che questo è un problema tanto per l'esecuzione di programmi da shell quanto per l'esecuzione di programmi da gestori di servizi, con gli utenti che vedono i programmi terminare e causare un altro prompt della shell quasi immediatamente. Infatti, proprio oggi qualcuno stava chiedendo, ancora una volta , sull'esecuzione di programmi dalla shell che esegue il fork e l'uscita da genitore, su Perché a volte quando eseguo un programma nel terminale, non verrà eseguito nel terminale?. )

Type=oneshotprobabilmente non è ciò che si desidera in questo caso particolare, poiché il servizio è considerato pronto solo quando l'intero programma di servizio è stato completato. Ha i suoi usi, ma a giudicare dai suoni non si applicano a te.

Non usare mai Type=forking. Dovrebbe essere l'ultima risorsa di disperazione, poiché quasi nessun programma parla effettivamente il protocollo . Stanno facendo qualcos'altro , che in realtà non è questo protocollo, non correttamente interoperabile con questo protocollo, e di fatto non segnalano disponibilità.

Ulteriore lettura

  • Jonathan de Boyne Pollard (2001). Errori da evitare durante la progettazione di programmi demone Unix . Risposte date frequentemente.
  • Jonathan de Boyne Pollard (2015). Non hai davvero bisogno di demonizzare. Veramente. . La Systemd House of Horror.
  • Jonathan de Boyne Pollard (2015). Problemi con il protocollo di prontezza con i demoni Unix . Risposte date frequentemente.
  • https://unix.stackexchange.com/a/401611/5132