Durante nossas aulas de Ciências da Computação do dia 122, exploramos os conceitos fundamentais de processos e threads no contexto de sistemas operacionais. Vimos como o sistema gerencia a execução de programas, a diferença entre processo e thread, e os mecanismos de criação e comunicação entre eles. Este artigo resume os principais tópicos discutidos em sala.

O que é um processo?

Um processo pode ser definido como um programa em execução. Ele contém o código do programa, seus dados, variáveis, pilha, contador de programa e os recursos alocados pelo sistema operacional (como arquivos abertos, conexões de rede, etc.). Cada processo possui seu próprio espaço de endereçamento, isolado de outros processos, garantindo proteção e estabilidade.

O sistema operacional mantém uma estrutura chamada PCB (Process Control Block) para gerenciar cada processo. O PCB armazena informações como:

  • Identificador do processo (PID);
  • Estado do processo;
  • Contador de programa;
  • Registradores da CPU;
  • Lista de arquivos abertos;
  • Informações de escalonamento.

Estados de um processo

Durante seu ciclo de vida, um processo pode assumir os seguintes estados:

  • Novo: o processo está sendo criado;
  • Pronto: está na memória e aguarda a CPU;
  • Executando: está utilizando a CPU;
  • Bloqueado: aguarda algum evento (E/S, sinal) para continuar;
  • Terminado: finalizou sua execução.

As transições entre esses estados são gerenciadas pelo escalonador do sistema operacional.

O que é uma thread?

Uma thread (ou linha de execução) é a menor unidade de processamento que pode ser escalonada pelo sistema operacional. Diferentemente de um processo, múltiplas threads dentro do mesmo processo compartilham o mesmo espaço de endereçamento (código, dados, heap) e podem se comunicar diretamente pela memória compartilhada. Cada thread possui sua própria pilha e registradores, mas divide os recursos do processo pai.

Threads são conhecidas como "processos leves" (lightweight processes) porque sua criação e troca de contexto são mais rápidas que a de processos completos.

Tipos de threads

  • Threads de nível de usuário: gerenciadas por uma biblioteca em espaço de usuário, sem suporte direto do kernel. A troca entre threads não requer chamadas de sistema, sendo muito rápida.
  • Threads de nível de kernel: gerenciadas diretamente pelo sistema operacional. Cada thread é escalonada individualmente, permitindo que threads de um processo executem em paralelo em múltiplos núcleos.

Criação de processos no Linux

No Linux, a chamada de sistema fork() cria um novo processo duplicando o processo atual. O processo original é chamado de pai e o novo é chamado de filho. Após o fork(), ambos continuam a execução a partir da próxima instrução, mas com valores de retorno diferentes: o pai recebe o PID do filho, e o filho recebe 0.

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

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        printf("Sou o processo filho (PID: %d)\n", getpid());
    } else if (pid > 0) {
        printf("Sou o processo pai, criei o filho %d\n", pid);
    } else {
        perror("Erro no fork");
    }
    return 0;
}

Para substituir a imagem do processo filho por um novo programa, usamos a família exec() (execlp, execvp, etc.). Combinando fork() e exec(), o shell implementa a execução de comandos.

Comunicação entre processos

Como os processos possuem espaços de endereçamento isolados, a comunicação entre eles (IPC – InterProcess Communication) exige mecanismos fornecidos pelo sistema operacional. Os principais são:

  • Pipes: canal unidirecional de comunicação entre processos relacionados (pai e filho). A saída de um processo é conectada à entrada de outro.
  • Named pipes (FIFO): similar ao pipe, mas permite comunicação entre processos não relacionados através de um arquivo especial.
  • Memória compartilhada: região de memória acessível por múltiplos processos, com sincronização via semáforos.
  • Message queues: filas de mensagens que permitem troca de dados estruturados entre processos.
  • Sockets: mecanismo de comunicação que pode ser usado entre processos na mesma máquina ou em redes distintas.

Sincronização

Quando múltiplos processos ou threads acessam recursos compartilhados (como variáveis globais ou arquivos), é necessário sincronizar o acesso para evitar condições de corrida (race conditions). Os mecanismos clássicos de sincronização incluem:

  • Mutex: lock de exclusão mútua. Apenas uma thread pode adquirir o mutex por vez.
  • Semáforo: contador que controla o acesso a um recurso compartilhado. Pode ser binário (0 ou 1) ou contador.
  • Variáveis de condição: permitem que threads aguardem até que uma condição específica seja satisfeita.

Em C, a biblioteca <pthread.h> fornece implementações de mutex e variáveis de condição para threads.

Principais diferenças entre processo e thread

CaracterísticaProcessoThread
Espaço de endereçamentoPróprio e isoladoCompartilhado com o processo pai
ComunicaçãoIPC (pipe, socket, etc.)Memória compartilhada diretamente
CriaçãoMais lenta (fork+exec)Mais rápida (pthread_create)
Troca de contextoMais custosaMais leve
IndependênciaAlta (falha de um não afeta os outros)Baixa (falha em uma thread pode derrubar o processo)
EscalonamentoPelo sistema operacionalPelo SO (threads de kernel) ou biblioteca (threads de usuário)

Perguntas Frequentes

Qual a diferença entre processo e thread?
Um processo possui seu próprio espaço de endereçamento independente, enquanto threads compartilham o mesmo espaço de endereçamento do processo que as criou. Threads são mais leves e a comunicação entre elas é mais simples.
O que é o PCB?
É o Process Control Block, uma estrutura de dados mantida pelo sistema operacional para armazenar informações sobre cada processo, como estado, registradores e lista de recursos alocados.
Como criar um processo em C no Linux?
Utilizando a chamada de sistema fork(), que duplica o processo atual. O novo processo (filho) continua a execução a partir da mesma instrução, mas com um valor de retorno diferente.
O que é um pipe?
É um mecanismo de comunicação unidirecional entre processos. Um processo escreve dados no pipe e outro lê esses dados. Pipes são comumente usados para conectar a saída de um comando à entrada de outro no shell.
Por que usar threads em vez de processos?
Threads são mais eficientes em termos de criação e troca de contexto, e permitem compartilhamento de dados sem mecanismos complexos de IPC. São ideais para aplicações que exigem paralelismo com necessidades de comunicação frequente, como servidores web e programas de computação gráfica.

Essas foram as anotações do dia 122. Continuaremos explorando sistemas operacionais nas próximas aulas, aprofundando em escalonamento, gerência de memória e sistemas de arquivos.