Perché questo programma stampa “forked!” 4 volte?

Perché questo programma stampa “forked!” 4 volte?

#include  #include  int main(void) { fork() && (fork() || fork()); printf("forked!\n"); return 0; } 

Il primo fork() restituisce un valore diverso da zero nel processo chiamante (chiamalo p0) e 0 nel child (chiamalo p1).

In p1 viene eseguito il cortocircuito per && e il processo chiama printf e termina. In p0 il processo deve valutare il resto dell’espressione. Quindi chiama di nuovo fork() , creando così un nuovo processo figlio (p2).

In p0 fork() restituisce un valore diverso da zero e il cortocircuito per || viene preso, quindi il processo chiama printf e termina.

In p2, fork() restituisce 0 così il resto del || deve essere valutato, che è l’ultimo fork() ; ciò porta alla creazione di un figlio per p2 (chiamalo p3).

P2 esegue quindi printf e termina.

P3 esegue quindi printf e termina.

4 printf vengono quindi eseguiti.

Quello viene da main() e gli altri tre da ogni fork() .

Si noti che tutti e tre i forks() verranno eseguiti. Potresti dare un’occhiata al ref :

VALORE DI RITORNO

Una volta completato con successo, fork () restituirà 0 al processo figlio e restituirà l’ID di processo del processo figlio al processo padre . Entrambi i processi devono continuare ad essere eseguiti dalla funzione fork (). Altrimenti, -1 deve essere restituito al processo genitore, non deve essere creato alcun processo figlio e errno deve essere impostato per indicare l’errore.

Si noti che l’id del processo non può essere zero, come indicato qui .


Quindi cosa succede veramente?

Abbiamo:

 fork() && (fork() || fork()); 

Quindi il primo fork() restituirà al padre l’id del processo diverso da zero, mentre restituirà 0 al processo figlio. Ciò significa che il primo fork dell’espressione logica verrà valutato su true nel processo padre, mentre nel processo figlio verrà valutato su false e, a causa della valutazione di Short circuit , non chiamerà i restanti due fork() s.

Quindi, ora sappiamo che otterranno almeno due stampe (una dal principale e una dal primo fork() ).

Ora, il 2 ° fork() nel processo padre sta per essere eseguito, lo fa e restituisce un valore diverso da zero al processo padre e uno zero nel processo figlio.

Così ora, il genitore non continuerà l’esecuzione fino all’ultimo fork() (a causa di cortocircuiti), mentre il processo figlio eseguirà l’ultimo fork, dal primo operando di || è 0.

Ciò significa che otterremo altre due stampe.

Di conseguenza, otteniamo quattro stampe in totale.


Cortocircuito

Qui, cortocircuito significa fondamentalmente che se il primo operando di && è zero, allora l’altro / i operando / i non viene valutato. Sulla stessa logica, se un operando di un || è 1, quindi il resto degli operandi non ha bisogno di essere valutato. Ciò accade perché il resto degli operandi non può modificare il risultato dell’espressione logica, quindi non è necessario che venga eseguito, quindi risparmiamo tempo.

Vedi l’esempio qui sotto.


Processi

Ricorda che un processo genitore crea processi di prole che a loro volta creano altri processi e così via. Ciò porta a una gerarchia di processi (o ad un albero che si potrebbe dire).

Tenendo presente questo, vale la pena dare un’occhiata a questo problema simile , così come questa risposta.


Immagine descrittiva

Ho fatto anche questa figura che può aiutare, immagino. Ho pensato che il pid’s fork() restituito fosse 3, 4 e 5 per ogni chiamata.

nodi di forcella Si noti che alcuni fork() hanno una X rossa sopra di loro, il che significa che non vengono eseguiti a causa della valutazione di cortocircuito dell’espressione logica.

I fork() in alto non verranno eseguiti, perché il primo operando dell’operatore && è 0, quindi l’intera espressione risulterà in 0, quindi nessuna essenza nell’esecuzione del resto dell’operando (s) di && .

Il fork() in basso non verrà eseguito, poiché è il secondo operando di un || , dove il suo primo operando è un numero diverso da zero, quindi il risultato dell’espressione è già valutato su true, indipendentemente dal secondo operando.

E nella prossima immagine puoi vedere la gerarchia dei processi: Gerarchia dei processi basato sulla figura precedente.


Esempio di cortocircuito

 #include  int main(void) { if(printf("A printf() results in logic true\n")) ;//empty body if(0 && printf("Short circuiting will not let me execute\n")) ; else if(0 || printf("I have to be executed\n")) ; else if(1 || printf("No need for me to get executed\n")) ; else printf("The answer wasn't nonsense after all!\n"); return 0; } 

Produzione:

 A printf() results in logic true I have to be executed 

Per tutti i downvoters, si tratta di una domanda unita ma diversa. Incolpare SO. Grazie.

È ansible scomporre il problema su tre righe, la prima e l’ultima riga raddoppiano semplicemente il numero di processi.

 fork() && fork() || fork(); 

Gli operatori stanno cortocircuitando, quindi questo è ciò che ottieni:

  fork() / \ 0/ \>0 || fork() && fork() /\ / \ / \ 0/ \>0 * * || fork() * / \ * * 

Quindi questo è complessivamente 4 * 5 = 20 processi ciascuno che stampa una riga.

Nota: se per qualche motivo fork () fallisce (ad esempio, hai un limite al numero di processi), restituisce -1 e quindi puoi ottenere risultati diversi.

Esecuzione di fork() && (fork() || fork()) , cosa succede

Ogni fork dà 2 processi con rispettivamente valori pid (padre) e 0 (figlio)

Prima forchetta:

  • il valore di ritorno genitore è pid not null => esegue il && (fork() || fork())
    • il valore genitore della seconda fork è pid e null non interrompe l’esecuzione di || part => stampa forked
    • second fork child value = 0 => esegue || fork() || fork()
      • stampe forked terza fork forked
      • stampe a forchetta di terza fork forked
  • il valore di ritorno figlio è 0 interrompe l’esecuzione di && part => stampe forked

Totale: 4 forked

Mi piacciono tutte le risposte che sono già state inviate. Forse se aggiungessi qualche altra variabile alla tua affermazione printf, sarebbe più facile per te vedere cosa sta succedendo.

 #include #include int main(){ long child = fork() && (fork() || fork()); printf("forked! PID=%ld Child=%ld\n", getpid(), child); return 0; } 

Sulla mia macchina ha prodotto questa uscita:

 forked! PID=3694 Child = 0 forked! PID=3696 Child = 0 forked! PID=3693 Child = 1 forked! PID=3695 Child = 1 

Questo codice:

 fork(); fork() && fork() || fork(); fork(); 

ottiene 20 processi per sé e 20 volte Printf andrà.

E per

 fork() && fork() || fork(); 

printf andrà un totale di 5 volte.