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ística | Processo | Thread |
|---|---|---|
| Espaço de endereçamento | Próprio e isolado | Compartilhado com o processo pai |
| Comunicação | IPC (pipe, socket, etc.) | Memória compartilhada diretamente |
| Criação | Mais lenta (fork+exec) | Mais rápida (pthread_create) |
| Troca de contexto | Mais custosa | Mais leve |
| Independência | Alta (falha de um não afeta os outros) | Baixa (falha em uma thread pode derrubar o processo) |
| Escalonamento | Pelo sistema operacional | Pelo 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.