# Forking de processos

Vamos começar com um exemplo simples que imprime "Hello" na saída padrão:

{% code title="forking.c" %}

```c
#include <stdio.h>

int main() {
	printf("Hello\n");
	return 0;
}
```

{% endcode %}

Primeiro passo é compilar o arquivo para um executável do SO:

```
$ gcc forking.c -o forking
```

Após isso, rodamos o executável e:

```
$ ./forking
Hello
```

> Dica: se quiser executar tudo em uma linha apenas, pode-se utilizar "gcc forking.c -o forking && ./forking"

*So far, so good.*

## O primeiro fork

Para fazer forking de processos em C, precisamos usar a syscall **fork** através do cabeçalho `unistd.h` (UNIX Standard), que fornece acesso a syscalls do Kernel, permitindo interagir diretamente com o sistema operacional:

{% code title="forking.c" %}

```c
#include <stdio.h>
#include <unistd.h>

int main() {
	fork();
	printf("Hello\n");
	return 0;
}
```

{% endcode %}

```
Hello
Hello
```

E por quê vemos agora a mensagem sendo impressa duas vezes? O quê acontece quando chamamos **fork**? Vamos detalhar o fluxo do programa:

* o processo pai é iniciado no começo do programa; apenas o pai está em execução
* quando o `fork` é chamado, o sistema cria um novo processo filho, que é *uma cópia exata do processo pai,* incluindo o estado do programa naquele exato momento
* qualquer código depois do fork é executado tanto no processo pai quanto no processo filho; como ambos chamam a função "printf" com "Hello", então a mensagem é impressa 2 vezes

Um ponto a destacar aqui é que, no momento do fork, todos os file descriptors do pai (incluindo o **STDOUT**) foram herdados pelo filho, e é por isso que vemos a mensagem na mesma saída padrão (tela do terminal) 2 vezes.

> É possível fazer com que o filho tenha outra saída padrão diferente do pai, mas não vamos entrar nestes detalhes aqui, não importam muito para o assunto principal que é concorrência

Se quisermos que cada um imprima uma mensagem diferente, teríamos que saber , de alguma forma **após a chamada do fork**, se estamos dentro do processo pai ou do filho.

## Manual, o nosso melhor amigo

De acordo com o [manual](https://man7.org/linux/man-pages/man2/fork.2.html), a  syscall fork tem o seguinte retorno:

> ```
> On success, the PID of the child process is returned in the
>        parent, and 0 is returned in the child.  On failure, -1 is
>        returned in the parent, no child process is created, and  is
>        set to indicate the error.
> ```

Ou seja, dentro do filho, o retorno da função é `0` . E dentro do pai, o retorno é o PID do filho. Com isto, podemos ter o seguinte código:

{% code title="forking.c" lineNumbers="true" %}

```c
#include <stdio.h>
#include <unistd.h>

int main() {
	pid_t pid = fork();
	if (pid == 0) {
		printf("Hello from child\n");
	} else {
		printf("Hello from parent\n");
	}
	return 0;
}
```

{% endcode %}

Que se executado, tem a seguinte saída:

```
Hello from parent
Hello from child
```

*Yay!*

Mas repare que, devido à natureza preemptiva do escalonador, não temos controle sobre qual será executado primeiro. Poderia ser o pai, ou mesmo o filho.&#x20;

O que temos que entender aqui é que, **no momento do fork**, é criado um *novo processo* no sistema operacional, que irá competir por recursos junto a outros processos,  ou seja, todo mundo junto no mesmo balaio de concorrência.

## Um exemplo mais robusto

Agora, vamos a um exemplo um pouco mais robusto com o uso de fork de processos, onde um processo pai dispara 3 processos filhos que irão executar uma tarefa que demora 2 segundos cada:

{% code title="forking-complex.c" lineNumbers="true" %}

```c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

void perform() {
	printf("Processo filho (PID: %d) executando tarefa...\n", getpid());
	sleep(2); // Simula uma tarefa que leva 2 segundos
	printf("Processo filho (PID: %d) completou a tarefa!\n", getpid());
}

int main() {
	int num_children = 3;
	pid_t pid;

	for (int i = 0; i < num_children; i++) {
		pid = fork(); // Cria um novo processo

		if (pid == 0) {
			// Processo filho
			perform();
			return 0;
		}
	}

	for (int i = 0; i < num_children; i++) {
		pid_t child_pid = wait(NULL); // Aguarda qualquer filho terminar
		printf("Pai: Processo filho com PID %d terminou.\n", child_pid);
	}

	printf("Pai: Todos os filhos terminaram. Finalizando.\n");
	return 0;
}
```

{% endcode %}

{% code lineNumbers="true" %}

```
Processo filho (PID: 271052) executando tarefa...
Processo filho (PID: 271053) executando tarefa...
Processo filho (PID: 271054) executando tarefa...
Processo filho (PID: 271052) completou a tarefa!
Processo filho (PID: 271053) completou a tarefa!
Pai: Processo filho com PID 271052 terminou.
Pai: Processo filho com PID 271053 terminou.
Processo filho (PID: 271054) completou a tarefa!
Pai: Processo filho com PID 271054 terminou.
Pai: Todos os filhos terminaram. Finalizando.
```

{% endcode %}

Interessante notar aqui:

* os 3 processos filhos foram criados e iniciaram sua execução (271052, 271053, 271054)
* 2 processos terminaram um pouco antes, sendo que o processo pai notificou que ambos tinham concluído execução
* um último processo (271054) terminou depois que o pai tinha notificado sobre os outros dois que concluíram primeiro
* o pai informa que todos os filhos foram concluídos e o programa finaliza

Isto, *senhoras e senhores*, é a maravilha da concorrência. Não temos controle algum sobre a ordem e execução das tarefas!

> Sim, vou repetir isso inúmeras vezes neste guia kk

## Comunicação entre processos (IPC)

Como processos não compartilham memória (a priori), precisamos de um mecanismo de comunicação entre processos, também chamado de **IPC** (Inter-process communication), pois um processo sem comunicação é bastante inútil.&#x20;

Uma das formas de IPC é através de **pipes,** que são uma comunicação *uni-direcional* entre processos.

> Se você quer entender mais sobre IPC e UNIX pipes, escrevi [um artigo sobre o tema](https://dev.to/leandronsp/entendendo-unix-pipes-3k56), onde é possível aprofundar nos conceitos de forma prática, e você só precisa de um terminal com shell/bash ou outra shell de preferência

```c
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main() {
    int pipe_fd[2];
    pid_t pid;

    pid = fork();

    if (pid == 0) {
        // Processo filho
        close(pipe_fd[0]); // Fecha a extremidade de leitura
        char mensagem[] = "Message from child!";
        // Escreve no pipe
        write(pipe_fd[1], mensagem, strlen(mensagem) + 1);
        close(pipe_fd[1]); // Fecha a extremidade de escrita
    } else {
        // Processo pai
        close(pipe_fd[1]); // Fecha a extremidade de escrita
        char buffer[100];
        // Lê a partir do pipe
        read(pipe_fd[0], buffer, sizeof(buffer));
        printf("Parent received message: %s\n", buffer);
        close(pipe_fd[0]); // Fecha a extremidade de leitura
    }

    return 0;
}
```

Este é um exemplo bastante simples de como 2 processos distintos podem conversar entre si, através da utilização de UNIX pipes.

## Tá gostando do guia?

Se está gostando deste trabalho e considera fortalecer, o QRCode de PIX abaixo tá no jeito hein?

<figure><img src="https://1347906346-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FADWkcUWoXJQdqxwxxE7z%2Fuploads%2F7xEda0Tzw1MwnyvSpRVg%2FScreenshot%202024-12-11%20at%2023.49.01.png?alt=media&#x26;token=00528cb5-27e7-45ba-b13f-5d6a7e20da3d" alt="" width="283"><figcaption><p><br></p></figcaption></figure>

Ou copia a cola:

```
00020126850014BR.GOV.BCB.PIX013638ee4bde-574b-4197-b10f-68742087b00b0223Gratidão pelo cafezinho5204000053039865802BR5925Leandro Freitas Maringolo6009SAO PAULO62140510qrN6Ov1wRl63041A3C
```

***

Não vamos entrar em mais detalhes sobre uso de forking em C, isto já é o suficiente para o objetivo deste guia. No próximo tópico, iremos abordar o uso de **threads em C**.
