Processos e estados
Um processo é um programa em execução — com sua própria memória, registradores e estado. O SO mantém um PCB (Process Control Block) para cada processo e os movimenta entre estados conforme eventos acontecem.
Processo ≠ programa
Um programa é um arquivo em disco (um conjunto de instruções). Um processo é esse programa em execução — com memória alocada, registradores, arquivos abertos, e um estado que muda ao longo do tempo. O mesmo programa pode gerar múltiplos processos (duas abas do Chrome são dois processos distintos).
O SO mantém um PCB (Process Control Block) para cada processo: uma estrutura de dados no kernel que contém o PID, o estado atual, os valores dos registradores (quando o processo não está executando), a tabela de páginas, a lista de arquivos abertos e muito mais.
Gerenciador de processos
Selecione um processo e use os botões para acionar transições de estado: fork (criar filho), escalonar (dar CPU), bloquear (aguardar I/O), desbloquear (I/O concluído) e terminar.
CPU está executando este processo agora (só 1 por core de CPU).
fork() e exec()
// fork() — criando um processo filho em C int main() { printf("PID do pai: %d\n", getpid()); // ex: 1234 pid_t pid = fork(); // aqui o universo se bifurca if (pid == 0) { // FILHO: pid retornado é 0 printf("Sou o filho! PID=%d, pai=%d\n", getpid(), getppid()); exec("/bin/ls"); // substitui imagem do processo por ls } else { // PAI: pid = PID do filho (ex: 1235) printf("Filho criado com PID=%d\n", pid); wait(NULL); // aguarda filho terminar (evita processo zumbi) printf("Filho terminou.\n"); } return 0; } // Output: // PID do pai: 1234 // Filho criado com PID=1235 // Sou o filho! PID=1235, pai=1234 // Filho terminou.
O que acontece num context switch
// Context switch: o que o kernel salva/restaura // Quando o escalonador troca de processo A para B: // 1. Salva contexto de A no PCB de A: pcb_A.registers = {rax, rbx, rcx, …, rsp, rbp, rip, rflags}; pcb_A.fpu_state = save_fpu(); // registradores FP/SSE/AVX pcb_A.cr3 = current_cr3; // ponteiro para page table de A // 2. Carrega contexto de B do PCB de B: restore_regs(pcb_B.registers); restore_fpu(pcb_B.fpu_state); write_cr3(pcb_B.cr3); // troca o espaço de endereçamento! // Escrever em CR3 invalida o TLB inteiro → overhead adicional // 3. CPU retoma B de onde parou (RIP aponta para próxima instrução) // Custo de um context switch: ~1–10 μs // Envolve: salvar ~100 bytes de registradores, trocar CR3, // invalidar TLB (ou usar ASID para evitar), atualizar estruturas do kernel. // Por isso goroutines (Go) e fibers são mais leves que threads OS.
wait(), o processo filho fica em estado "terminado" mas seu PCB permanece
na tabela de processos — o SO precisa manter o código de saída disponível para o pai.
Isso é um processo zumbi. Se o pai nunca chama wait(), o processo init (PID 1)
adota os filhos órfãos e faz o wait() por eles.
Criando e gerenciando processos
Mini projeto: escreva um programa C que usa fork() para criar 3 filhos em paralelo. Cada filho dorme um número diferente de segundos (sleep(n)) e imprime seu PID ao terminar. O pai deve usar wait() em loop até todos os filhos terminarem. Observe que a ordem de término não é determinística.
Projeto principal: implemente um mini shell: leia comandos do stdin (ls, echo, etc.), faça fork(), execute o comando com execvp() no filho, e o pai aguarda com waitpid(). Adicione suporte a pipes (|): use pipe() para conectar stdout de um processo ao stdin do próximo.
Desafio extra: use clone() diretamente (Linux) com diferentes flags de namespace para criar um processo isolado: CLONE_NEWPID (novo namespace de PID — o filho vê apenas seus filhos), CLONE_NEWNET (sem rede), CLONE_NEWMNT (filesystem privado). Isso é a base de como containers Docker funcionam.
Teste sua intuição
fork() faz e o que cada processo recebe como retorno?
Onde você encontra isso
Multi-process browsers
Chrome usa um processo por aba (site isolation). Se uma aba travar, não derruba as outras. O processo de aba roda com sandbox restrito (sem acesso a arquivos do SO direto). A comunicação entre processos é feita via IPC (Inter-Process Communication) com pipes e shared memory.
Containers e Docker
Docker usa namespaces Linux (PID, net, mnt, user) para isolar processos. Um container é um processo (ou grupo de processos) com uma visão restrita do sistema. cgroups (control groups) limitam CPU, memória e I/O. Não há hypervisor — é o mesmo kernel Linux, mas com fronteiras virtuais criadas pelo SO.
Gerenciadores de processos
top, htop e ps leem /proc/ — um sistema de arquivos virtual do Linux onde cada diretório /proc/PID/ expõe o PCB do processo como arquivos legíveis: status (estado, memória), maps (VMAs), fd/ (file descriptors abertos), cmdline (comando original).