Dado um programa na chamada de sistema fork() .

#include <stdio.h>
#include <unistd.h>
int main()
{
   fork();
   fork() && fork() || fork();
   fork();
  
   printf("forked\n");
   return 0;
}

Quantos processos serão gerados após a execução do programa acima?

Uma chamada de sistema fork() gera processos como folhas de uma árvore binária em crescimento. Se chamarmos fork() duas vezes, ele gerará 2 2 = 4 processos. Todos esses 4 processos formam os filhos folha da árvore binária. Em geral, se formos o nível l , e fork() for chamado incondicionalmente, teremos 2 l processos no nível ( l + 1 ). É equivalente ao número máximo de nós filhos em uma árvore binária no nível ( l + 1 ).

Como outro exemplo, suponha que invocamos a chamada fork() 3 vezes incondicionalmente. Podemos representar o processo gerado usando uma árvore binária completa com 3 níveis. No nível 3, teremos 2 3 = 8 nós filhos, o que corresponde ao número de processos em execução.

Uma observação sobre operadores lógicos C / C++:

O operador lógico && tem mais precedência do que ||, e tem associatividade da esquerda para a direita. Após a execução do operando esquerdo, o resultado final será estimado e a execução do operando direito dependerá do resultado do operando esquerdo e do tipo de operação.

No caso de AND (&&), após avaliação do operando esquerdo, o operando direito será avaliado apenas se o operando esquerdo for diferente de zero . No caso de OR (||), após avaliação do operando esquerdo, o operando direito será avaliado apenas se o operando esquerdo for zero .

Valor de retorno de fork():

As páginas de manual de fork() citam o seguinte trecho sobre o valor de retorno,

Em caso de sucesso, o PID do processo filho é retornado no pai e 0 é retornado no filho. Em caso de falha, -1 é retornado no pai, nenhum processo filho é criado e errno é definido apropriadamente.

Um PID é como um identificador de processo e representado como um int sem sinal . Podemos concluir que o fork() retornará um diferente de zero no pai e zero no filho. Vamos analisar o programa. Para facilitar a notação, rotule cada garfo() conforme mostrado abaixo,

#include <stdio.h>
int main()
{
   fork(); /* A */
   ( fork()  /* B */ &&
     fork()  /* C */ ) || /* B and C are grouped according to precedence */
   fork(); /* D */
   fork(); /* E */

   printf("forked\n");
   return 0;
}

O diagrama a seguir fornece uma representação pictórica de novos processos de bifurcação. Todos os processos recém-criados são propagados no lado direito da árvore e os pais são propagados no lado esquerdo da árvore, em níveis consecutivos.

As duas primeiras chamadas fork() são chamadas incondicionalmente.

No nível 0, temos apenas o processo principal. O principal (m no diagrama) criará o filho C1 e ambos continuarão a execução. Os filhos são numerados em ordem crescente de criação.

No nível 1, temos m e C1 em execução e prontos para executar fork() - B. (Observe que B, C e D são nomeados como operandos dos operadores && e ||). A expressão inicial B será executada em cada filho e processo pai em execução neste nível.

No nível 2, devido ao fork() - B executado por me C1, temos m e C1 como pais e C2 e C3 como filhos.

O valor de retorno de fork() - B é diferente de zero no pai e zero no filho. Como o primeiro operador é &&, por causa do valor de retorno zero, os filhos C2 e C3  não executarão a próxima expressão (fork() - C). Os processos pais me C1 continuarão com fork() - C. Os filhos C2 e C3 irão executar fork() - D diretamente, para avaliar o valor da operação lógica OR.

No nível 3, temos m, C1, C2, C3 como processos em execução e C4, C5 como filhos. A expressão agora é simplificada para ((B && C) || D) e, neste ponto, o valor de (B && C) é óbvio. Nos pais, é diferente de zero e nos filhos, é zero. Conseqüentemente, os pais estão cientes do resultado geral de B && C || D, irá pular a execução de fork() - D. Uma vez que, nos filhos (B && C) avaliados para zero, eles irão executar fork() - D. Devemos notar que os filhos C2 e C3 criados no nível 2, também irão execute fork() - D conforme mencionado acima.

No nível 4, teremos m, C1, C2, C3, C4, C5 como processos em execução e C6, C7, C8 e C9 como processos filhos. Todos esses processos executam fork() - E incondicionalmente e geram um filho.

No nível 5, teremos 20 processos em execução. O programa (no Ubuntu Maverick, GCC 4.4.5) imprimiu “bifurcado” 20 vezes. Uma vez pelo pai raiz (principal) e o resto pelos filhos. Ao todo, serão 19 processos gerados.

Uma nota sobre a ordem de avaliação:

A ordem de avaliação das expressões em operadores binários não é especificada. Para obter detalhes, leia o post  Avaliação da ordem dos operandos . No entanto, os operadores lógicos são uma exceção. Eles são garantidos para avaliar da esquerda para a direita.

Contribuição de  Venki . Escreva comentários se encontrar algo incorreto ou se quiser compartilhar mais informações sobre o tópico discutido acima.

Quer aprender com os melhores vídeos com curadoria e problemas práticos, confira o C++ Foundation Course for Basic to Advanced C++ e C++ STL Course for Foundation plus STL. Para completar sua preparação desde o aprendizado de um idioma até o DS Algo e muitos mais, consulte o Curso Completo de Preparação para Entrevistas .