[13]. Que recursos são usados quando um thread é criado? Como eles diferem daqueles usados quando um processo é criado?
Passo 1
O que o enunciado pede:
- Identificar quais recursos são alocados/instanciados quando um thread (fio de execução) é criado.
- Explicar em que esses recursos diferem dos alocados quando um processo é criado.
Informações relevantes (o que há implicitamente no enunciado):
- Não foi pedido um comando/sistema operacional específico, então vou explicar de forma geral (com exemplos típicos em sistemas UNIX/Linux).
- Vou assumir o modelo usual: processo = entidade com espaço de endereço próprio; thread = unidade de execução dentro de um processo que normalmente compartilha recursos com outras threads do mesmo processo.
Passo 2
O que é cada coisa (pequena aula para ficar claro)
- Processo: é a unidade que tem seu próprio espaço de memória virtual (código, dados, heap, pilha do processo), identificador único (PID), tabela de descritores de arquivos/recursos, atributos de usuário/permissões etc. Ele contém um ou mais threads.
- Thread: é uma linha de execução dentro de um processo. Cada thread tem seu próprio contador de programa, registros de CPU e pilha, mas normalmente compartilha o espaço de memória e muitos recursos do processo (arquivos abertos, memória, sinalizadores) com as outras threads do mesmo processo.
Explicação simples do porquê: threads existem para permitir execução concorrente sem o custo de duplicar todo o processo — eles compartilham o que é caro (memória) e isolam o que precisa ser isolado (estado da execução).
Passo 3
Recursos tipicamente alocados quando um processo é criado (resumo didático)
- Identificador do processo (PID) e entrada na tabela de processos (PCB / task_struct): estrutura do kernel que guarda estado do processo (estado, contadores, ponteiros para memória, etc.).
- Espaço de endereço virtual separado: tabelas de páginas / estruturas de gerenciamento de memória (um “mm_struct” no Linux). Isso inclui cópia (ou COW) de segmentos de código/dados/heap.
- Pilha do processo: cada processo tem ao menos uma pilha de usuário (para a thread principal) e uma pilha do kernel (usada nas chamadas ao sistema).
- Tabela de descritores de arquivos: normalmente é copiada (os descritores apontam para os mesmos objetos de arquivo, mas a tabela em si é uma cópia no fork); resources como sockets/arquivos são referenciados.
- Sessões, grupos de processos, identificadores de usuário, máscaras de sinal, atributos de ambiente (variáveis de ambiente, diretório de trabalho).
- Recursos do kernel associados: contadores de uso, estruturas de contabilidade, quotas, limites (ulimit).
- Se for create+exec: carregar novo binário, configurar segmentos de código/dados, inicializar a stack do usuário com argv/envp.
- Custo prático: criar um processo costuma ser “pesado” — mais alocação e configuração (embora técnicas como copy-on-write reduzam custo imediato).
Passo 4
Recursos tipicamente alocados quando um thread é criado (resumo didático)
- Identificador do thread (TID ou equivalente) e uma estrutura de controle de thread (TCB) que o kernel/rt library mantém: guarda contexto da thread (estado, ponteiros para pilha, prioridade).
- Pilha de usuário própria para a thread: cada thread precisa da sua pilha para chamadas de função/variáveis locais.
- Pilha do kernel (ou área de contexto para traps/syscalls) para a thread: quando a thread entra no kernel ela usa uma pilha kernel própria ou um slot por thread.
- Conjunto de registros da CPU / contexto (PC, SP, registradores) — usado no escalonamento e trocas de contexto.
- Espaço para dados específicos da thread (Thread-Local Storage, TLS): área onde variáveis “thread-local” são guardadas.
- Entradas de agendamento: prioridade, política de escalonamento e contadores de tempo para essa thread.
- Recursos de sincronização associados podem ser criados (ex.: mutexes próprios, condvars alocados pela biblioteca).
- NOTA: o thread normalmente não cria um novo espaço de memória virtual; ele compartilha:
- o mesmo espaço de código e dados (mesmas páginas),
- a mesma tabela de descritores de arquivos,
- a mesma heap,
- as mesmas informações de processo (UID, diretório de trabalho, etc.).
- Custo prático: criar um thread é geralmente “barato” comparado a criar um processo — alocações pequenas (pilha, TCB) e registro no escalonador.
Passo 5
Comparação ponto-a-ponto (o que é compartilhado vs duplicado)
- Espaço de endereço / memória:
- Processo: separado (próprio); ao criar, o kernel cria ou duplica estruturas de mapeamento de páginas.
- Thread: compartilhado entre threads do mesmo processo (mesmas páginas).
- Pilha:
- Processo: a pilha principal do processo é parte do novo espaço de usuário; processos adicionais têm suas próprias pilhas.
- Thread: cada thread tem sua própria pilha de usuário (e kernel stack).
- Registros / contador de programa:
- Processo: a thread principal do novo processo tem seu próprio contexto; processos diferentes têm contextos separados.
- Thread: cada thread tem seu próprio contexto (PC, registradores).
- Tabela de descritores de arquivo:
- Processo: normalmente é copiada (no fork) — a tabela do filho aponta para os mesmos objetos de arquivo; em exec substitui-se por outro binário.
- Thread: totalmente compartilhada entre threads do mesmo processo.
- Identificadores:
- Processo: PID distinto.
- Thread: TID distinto; em sistemas UNIX modernos os threads pertencem ao mesmo PID lógico mas têm TIDs separados internamente.
- Estruturas de kernel (PCB / task_struct / mm_struct):
- Processo: precisa de uma mm_struct (gestão de memória) exclusiva e de um task_struct completo.
- Thread: tem uma task_struct/TCB para cada thread, mas pode referenciar a mesma mm_struct; menos estruturas pesadas que um processo novo.
- Sinais:
- Processo: sinais e tratadores são associados ao processo; dependendo do sistema, alguns aspectos dos sinais são compartilhados ou por-thread.
- Thread: manejo de sinais é mais fino — em POSIX, a disposição dos sinais é do processo, a máscara de sinais pode ser por-thread; alguns sinais são direcionáveis a threads específicas.
- Permissões/UID/GID/ambiente:
- Processo: cada processo tem esses atributos (filhos herdam).
- Thread: compartilham com o processo.
- Overhead de criação:
- Processo: mais alto (alocar/instanciar mm, possivelmente carregar binário).
- Thread: menor (alocar pilha + TCB + registrar ao escalonador).
Passo 6
Aspecto prático: custo e desempenho
- Criação: criar threads é mais rápido e usa menos memória porque não é preciso construir um novo espaço de endereçamento nem duplicar estruturas pesadas.
- Troca de contexto: trocar entre threads do mesmo processo costuma ser mais rápida (menos TLB shootdown, mesmos mapeamentos de página) do que entre processos.
- Comunicação: threads podem comunicar-se por memória compartilhada sem mecanismos IPC caros; processos normalmente precisam de pipes, sockets, shared memory explicitamente.
- Isolamento: processos oferecem isolamento (falha/memória não corrompe outro processo); threads correm o risco de um erro na memória corromper o processo inteiro.
Passo 7
Comportamento em falhas e segurança (por que importa)
- Se uma thread sofre um segmentation fault (acesso inválido à memória), isso normalmente derruba o processo inteiro — porque a memória é compartilhada.
- Se um processo filho morre, o pai pode continuar (isolamento); threads não isolam deste modo.
- Limites de recursos (por exemplo, limites de número de arquivos abertos) são em geral aplicados ao processo, então threads competem pelos mesmos recursos.
Passo 8
Exemplos práticos e mneumônicos (para lembrar na prova)
- Para lembrar: “threads = compartilham o grande (memória, arquivos), têm o pequeno próprio (pilha, registradores)”.
- Exemplo rápido:
- Criar 1000 processos -> cada um com mm_struct, tabelas de páginas, overhead grande.
- Criar 1000 threads -> 1000 pilhas + 1000 TCBs, mas uma única mm_struct compartilhada -> muito mais leve.
- Comando/sistema: em Unix típico:
- fork()/exec() cria processo (fork duplica; exec substitui imagem).
- pthread_create() cria thread (ou clone() com flags apropriadas no Linux).
Passo 9
Resumo final das diferenças mais importantes (para decorar e usar na prova)
- Threads:
- Compartilham: espaço de endereço, heap, código, descritores de arquivos, ambiente, permissões.
- Possuem: pilha própria, contexto (registradores), TID/TCB, TLS, kernel stack.
- Criação/cambio de contexto: barato.
- Comunicação: simples (memória compartilhada).
- Isolamento: fraco (um erro afeta todo o processo).
- Processos:
- Compartilham pouco por padrão (somente o que é explicitamente herdado).
- Possuem: espaço de endereço próprio, tabelas de páginas/mm exclusivos, PID, PCB completo, cópias ou instâncias de tabelas (file table copy-on-write).
- Criação/cambio de contexto: mais caro.
- Comunicação: requer IPC mais explícito.
- Isolamento: forte.
Resposta
Recursos alocados na criação de um thread: identificador de thread (TID) e estrutura de controle da thread (TCB/task entry), pilha de usuário específica, pilha de kernel ou área de contexto para syscalls, contexto de CPU (registradores/PC), thread-local storage (TLS), entradas de agendamento (prioridade/política) e eventuais estruturas de sincronização; o thread normalmente reutiliza o mm (espaço de endereçamento), a tabela de descritores de arquivo, ambiente, UID/GID e outros recursos do processo pai.
Diferenças principais em relação à criação de um processo: criação de processo aloca também um PID e uma PCB completa, um novo espaço de endereço virtual (estruturas de página/mm), possivelmente cópia (ou COW) das tabelas de descritores de arquivo, carregamento do binário (em exec), e mais estruturas de kernel/contabilidade — logo processos são muito mais pesados em memória e tempo para criar e trocar contexto; threads compartilham a maior parte do estado do processo e custam muito menos para criar e escalonar, mas oferecem menos isolamento (um erro de memória numa thread compromete todo o processo).