Perché fork () dovrebbe essere stato progettato per restituire un descrittore di file?


16

Nella sua pagina web sull'inganno del self-pipe , Dan Bernstein spiega una condizione di gara select()e segnala, offre una soluzione alternativa e conclude che

Naturalmente, la cosa giusta sarebbe quella di fork()restituire un descrittore di file, non un ID processo.

Che cosa intende con questo: è qualcosa che riguarda la capacità select()dei processi figlio di gestire i loro cambiamenti di stato invece di dover usare un gestore di segnale per essere avvisati di quei cambiamenti di stato?


L'articolo viene confuso con l'input e l'output o non lo sto leggendo correttamente?
ctrl-alt-delor,

Puoi chiedere che i segnali vengano inviati sui tubi. Questo è quello che faccio.
ctrl-alt-delor,

@ ctrl-alt-delor, sì, sembra che usi "input / output pipe" un po 'stranamente, ma penso che sia chiaro dove sta scrivendo e dove leggere da una pipe. Quel testo è dal 2003, e non sono sicuro signalfds e come fosse una cosa allora?
ilkkachu,

5
Dan sa di cosa sta parlando, anche se può essere un po 'deliberatamente provocatorio. Se fossi deliberatamente provocatorio, opterei per il fatto che, ovviamente, la cosa giusta sarebbe sbarazzarsi di SIGCHLD.
Steve Summit,

1
@mosvy Sto esagerando leggermente, ma ogni programma e ogni programmatore che abbia mai visto chi ha provato ad usare SIGCHLD ha avuto problemi con esso. È una condizione di gara in attesa di accadere. Quando tutto ciò che avevamo era il blocco wait(), c'erano cose che non potevi fare, quindi qualcuno ha inventato SIGCHLD, ma è stato un brutto lavoro. Nella mia esperienza, e ora che esistono, spruzzando bello, non bloccante wait3(), wait4(), e / o waitpid()le chiamate in luoghi chiave (forse il vostro anello evento principale) è un gran lunga superiore alternativa.
Steve Summit,

Risposte:


14

Il problema è descritto lì nella tua fonte, select()dovrebbe essere interrotto da segnali simili SIGCHLD, ma in alcuni casi non funziona così bene. Quindi la soluzione alternativa è quella di fare scrivere il segnale su una pipe, che viene quindi guardato da select(). Guardare i descrittori di file è ciò che select()serve, in modo che aggiri il problema.

La soluzione alternativa essenzialmente trasforma l'evento signal in un evento descrittore di file. Se fork()in primo luogo fosse appena restituito un fd, la soluzione alternativa non sarebbe richiesta, poiché tale fd potrebbe quindi essere presumibilmente utilizzato direttamente con select().

Quindi sì, la tua descrizione nell'ultimo paragrafo mi sembra giusta.


Un altro motivo per cui un fd (o qualche altro tipo di handle del kernel) sarebbe migliore di un semplice numero ID di processo, è che i PID possono essere riutilizzati dopo la fine del processo. Questo può essere un problema in alcuni casi quando si inviano segnali ai processi, potrebbe non essere possibile sapere con certezza che il processo è quello che pensi sia, e non un altro che riutilizza lo stesso PID. (Anche se penso che questo non dovrebbe essere un problema quando si inviano segnali a un processo figlio, dal momento che il genitore deve correre wait()sul figlio perché il suo PID venga rilasciato.)


Detto questo, non ricordo esattamente i casi che ho letto sul riutilizzo del PID come un problema, quindi se qualcuno vuole approfondire o chiarire questo, o addirittura modificare quanto sopra, non esitate a farlo.
ilkkachu,

2
Come hai già detto, non ci sono scuse per un genitore che ritenga che il proprio pid figlio sia stato riutilizzato. Ha il pieno controllo di quella situazione perché è colui che chiama wait().
Giosuè,

Questi sono chiamati processi zombie : "un processo che ha completato l'esecuzione ma ha ancora una voce nella tabella dei processi: è un processo nello" Stato terminato ". Ciò si verifica per i processi figlio, in cui la voce è ancora necessaria per consentire al genitore processo per leggere lo stato di uscita di suo figlio "
Lassi,

6
Vale la pena ricordare che ora Linux può restituire un descrittore di file (pidfd) clone, che è l'effettiva chiamata di sistema che il fork invoca su LInux. Il flag per abilitare questo è chiamato CLONE_PIDFD- Vedi ad esempio lwn.net/Articles/784831 .
KJ Tsanaktsidis,

1
@Lie Ryan, Re " Avere fork () restituisce un handle globale anziché un handle locale è concettualmente più corretto perché ", Windows utilizza gli handle di processo. Il codice pid e di uscita restano attivi fino a quando tutti gli handle del processo non vengono chiusi (anziché attendere che il genitore raccolga), evitando le condizioni di competizione comuni nei sistemi unix. Quando gli handle mantengono vivo il processo, ha molto più senso che siano handle locali anziché globali.
ikegami,

9

È solo una riflessione sulle linee di "sarebbe bello se Unix fosse progettato diversamente da come è".

Il problema con i PID è che vivono in uno spazio dei nomi globale in cui potrebbero essere riutilizzati per un altro processo, e sarebbe bello se fork()restituito nel genitore un tipo di handle che sarebbe garantito per fare sempre riferimento al processo figlio e che potrebbe passare ad altri processi tramite ereditarietà o unix socket / SCM_RIGHTS[1].

Vedi anche la discussione qui per un recente sforzo per "aggiustare" quello in Linux, inclusa l'aggiunta di un flag a clone()cui lo farà restituire un pid-fd invece di un PID.

Ma anche allora, ciò non eliminerebbe la necessità di quell'hacking self-pipe [2] o di interfacce migliori, poiché i segnali che informano un processo genitore sullo stato di un bambino non sono i soli che vorresti gestire nel ciclo principale del programma. Sfortunatamente, cose come epoll(7) + signalfd(2)su Linux o kqueue(2)su BSD non sono standard: l'unica interfaccia standard (ma non supportata su sistemi più vecchi) è molto inferiore pselect(2).

[1] Prevenire il riciclo del PID entro il tempo in cui la waitpid()syscall era tornata e il suo valore di ritorno usato poteva essere probabilmente ottenuto su sistemi più recenti usando waitid(.., WNOWAIT)invece.

[2] Non vorrei commentare l'affermazione di DJ Bernstein di averlo inventato (scusate l'apofasi ;-)).


8

Bernstein non dà molto contesto per questa osservazione "Right Thing", ma rischierò un'ipotesi: avere fork (2) restituire un PID non è coerente con open (2), creat (2) ecc. Che restituiscono i descrittori di file. Il resto del sistema Unix avrebbe potuto eseguire la manipolazione del processo con un descrittore di file che rappresenta un processo, anziché un PID. Esiste una chiamata di sistema signalfd (2) , che consente un'interazione leggermente migliore tra segnali e descrittori di file e mostra che un descrittore di file che rappresenta un processo potrebbe funzionare.


signalfd (2) sembra fantastico, grazie per averlo menzionato! Peccato che sia solo per Linux.
Lassi,

1
Ci sono state discussioni pidfd_openanche su Linux, vedi ad esempio lwn.net/Articles/789023
dhag
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.