Sistemas Computacionais: Uma Perspectiva do Programador (Edição Global)
Uma análise aprofundada sobre como os sistemas computacionais executam programas e armazenam informações. Este curso pontua a lacuna entre a programação de alto nível e o hardware subjacente, abrangendo representação em nível de máquina, arquitetura de processadores, hierarquia de memória e programação concorrente.
Visão Geral do Curso
📚 Resumo do Conteúdo
Uma análise aprofundada sobre como sistemas computacionais executam programas e armazenam informações. Este curso pontua a lacuna entre programação de alto nível e o hardware subjacente, abordando representações em nível de máquina, arquitetura de processadores, hierarquia de memória e programação concorrente.
Domine a arte da programação de sistemas compreendendo a interface entre hardware e software.
Autor: Randal E. Bryant, David R. O'Hallaron
Agradecimentos: Apoiado pelos alunos e instrutores do curso 15-213 na Universidade Carnegie Mellon. Os agradecimentos incluem contribuições de Manasa S. e Mohit Tahiliani.
🎯 Objetivos de Aprendizagem
- Identificar como as informações são representadas usando bits e contexto dentro de um sistema.
- Traçar as quatro fases do sistema de compilação desde o código-fonte até o executável.
- Descrever a estrutura organizacional do hardware e a natureza hierárquica dos dispositivos de armazenamento.
- Converter entre notações decimal, binária e hexadecimal e explicar endereçamento em nível de máquina (Endianness).
- Realizar operações a nível de bit e lógicas em C e prever os resultados de deslocamentos aritméticos.
- Analisar codificações de inteiros para identificar vulnerabilidades potenciais a estouro e erros de conversão.
- Analisar o mapeamento entre construtos C (laços, ramificações, procedimentos) e instruções assembly x86-64.
- Desmontar a pilha de tempo de execução para explicar como parâmetros são passados, variáveis locais são armazenadas e chamadas recursivas são gerenciadas.
- Avaliar layouts de memória para estruturas de dados heterogêneas e aplicar regras de alinhamento para calcular requisitos totais de armazenamento.
- Definir o Estado Visível ao Programador Y86-64 e codificar/decodificar instruções em sequências de bytes.
🔹 Lição 1: Uma Visita aos Sistemas Computacionais
Visão Geral: Esta lição oferece uma visão abrangente sobre como sistemas computacionais representam informações, traduzem programas e executam instruções por meio de interações complexas entre hardware e software. Explora a jornada de um programa desde o código-fonte até a execução, o papel crítico da hierarquia de memória no superamento da lacuna entre processador e memória, as abstrações fornecidas pelo sistema operacional e as leis matemáticas que governam o desempenho e a paralelização do sistema.
Resultados de Aprendizagem:
- Identificar como as informações são representadas usando bits e contexto dentro de um sistema.
- Traçar as quatro fases do sistema de compilação desde o código-fonte até o executável.
- Descrever a estrutura organizacional do hardware e a natureza hierárquica dos dispositivos de armazenamento.
🔹 Lição 2: Representação e Manipulação de Informações
Visão Geral: Esta lição explora como computadores digitais representam e manipulam informações a nível de bit. Aborda a transição da notação hexadecimal e tamanhos de palavra em nível de máquina até as codificações complexas de inteiros (sem sinal e complemento de dois) e números de ponto flutuante (IEEE 754). Os alunos analisarão as propriedades matemáticas da aritmética computacional, incluindo implicações de segurança do estouro e nuances do arredondamento em sistemas de precisão finita.
Resultados de Aprendizagem:
- Converter entre notações decimal, binária e hexadecimal e explicar endereçamento em nível de máquina (Endianness).
- Realizar operações a nível de bit e lógicas em C e prever os resultados de deslocamentos aritméticos.
- Analisar codificações de inteiros para identificar vulnerabilidades potenciais a estouro e erros de conversão.
🔹 Lição 3: Representação em Nível de Máquina de Programas
Visão Geral: Esta lição oferece uma análise aprofundada sobre como programas C de alto nível são transformados em código de máquina x86-64. Aborda a arquitetura fundamental do processador, incluindo registradores e pilha, a implementação de fluxo de controle (condicionais, laços e switches), a mecânica de chamadas de procedimentos e recursão, e a representação em nível de máquina de estruturas de dados complexas como arrays, structs e unions. Finalmente, aborda segurança de sistema via análise de buffer overflow e as instruções especializadas usadas para aritmética de ponto flutuante.
Resultados de Aprendizagem:
- Analisar o mapeamento entre construtos C (laços, ramificações, procedimentos) e instruções assembly x86-64.
- Desmontar a pilha de tempo de execução para explicar como parâmetros são passados, variáveis locais são armazenadas e chamadas recursivas são gerenciadas.
- Avaliar layouts de memória para estruturas de dados heterogêneas e aplicar regras de alinhamento para calcular requisitos totais de armazenamento.
🔹 Lição 4: Arquitetura de Processadores
Visão Geral: Esta lição explora a arquitetura fundamental de um processador, focando na transição de uma implementação sequencial (SEQ) para uma implementação pipelina de alto desempenho (PIPE) usando a Arquitetura de Conjunto de Instruções Y86-64 (ISA). Os alunos analisarão como instruções são codificadas, processadas em estágios discretos (Busca, Decodificação, Execução, Memória, Escrita-Back) e como hazards de hardware são gerenciados com lógica de controle, espera e encaminhamento para maximizar o throughput.
Resultados de Aprendizagem:
- Definir o Estado Visível ao Programador Y86-64 e codificar/decodificar instruções em sequências de bytes.
- Implementar lógica de controle de hardware usando HCL (Linguagem de Controle de Hardware) para circuitos combinacionais e sequenciais.
- Traçar o fluxo de instruções através dos seis estágios de um processador sequencial e identificar o impacto da clocking.
🔹 Lição 5: Otimização do Desempenho de Programas
Visão Geral: Esta lição explora a abordagem sistemática para melhorar o desempenho de programas, entendendo a interação entre código de alto nível, compiladores otimizadores e arquiteturas modernas de microprocessadores. Os alunos aprenderão a identificar "blocos de otimização", como alias de memória, aplicar transformações de baixo nível como desenrolamento de laços e reassociação, e utilizar ferramentas de perfilagem como GPROF para atacar eficazmente gargalos de desempenho.
Resultados de Aprendizagem:
- Identificar e mitigar blocos de otimização, incluindo alias de memória e sobrecarga de chamadas de procedimento.
- Quantificar o desempenho do programa usando a métrica Cycles Per Element (CPE).
- Aplicar transformações de desenrolamento de laço, múltiplos acumuladores e reassociação para explorar paralelismo em nível de instrução.
🔹 Lição 6: A Hierarquia de Memória
Visão Geral: Esta lição explora o design estrutural e funcional da hierarquia de memória, focando nos trade-offs entre velocidade, custo e capacidade de armazenamento. Detalha as tecnologias que impulsionam sistemas modernos — desde SRAM e DRAM até Discos e SSDs — e explica como o Princípio da Localidade (Temporal e Espacial) permite que pequenas memórias cache rápidas aumentem significativamente o desempenho do programa. Os alunos aprenderão a analisar mapeamento de cache (Direto, Associativo por Conjunto, Totalmente Associativo) e aplicar técnicas de otimização como reordenamento de laços e bloqueio para escrever código amigável à cache.
Resultados de Aprendizagem:
- Distinguir entre tecnologias de memória SRAM, DRAM, ROM e Flash e seus papéis na hierarquia.
- Calcular a capacidade de armazenamento de disco e o tempo total de acesso com base na geometria e componentes operacionais.
- Analisar endereços de memória para determinar índices de conjunto de cache, tags e deslocamentos de bloco em diferentes estratégias de mapeamento.
🔹 Lição 7: Ligação
Visão Geral: Esta lição explora o processo crítico de ligação a nível de sistema, que agrupa código e dados em um único arquivo que pode ser carregado na memória e executado. Os alunos passarão do código-fonte ao binário executável, compreendendo como os ligadores resolvem referências de símbolos, unem seções por meio de relocação e gerenciam bibliotecas estáticas e dinâmicas. A lição termina com técnicas avançadas como interposição de bibliotecas e código independente de posição (PIC) usadas em bibliotecas compartilhadas modernas.
Resultados de Aprendizagem:
- Traçar a transformação de arquivos-fonte através do driver do compilador até um executável final.
- Analisar arquivos-objeto ELF para identificar tipos de símbolos e organização de seções.
- Aplicar regras de resolução de símbolos para gerenciar nomes duplicados e dependências em tempo de ligação.
🔹 Lição 8: Fluxo de Controle Excepcional
Visão Geral: Esta lição explora o Fluxo de Controle Excepcional (ECF), o mecanismo pelo qual um sistema computacional reage às mudanças no estado do sistema. Examinamos como o ECF é implementado em todos os níveis do sistema, desde exceções disparadas por hardware (Exceptions), trocas de contexto e controle de processos no nível do sistema operacional (fork, wait, execve) até sinais e saltos não locais no nível de software. Os alunos aprenderão a gerenciar concorrência, lidar com erros do sistema e escrever código robusto e seguro contra sinais.
Resultados de Aprendizagem:
- Distinguir entre as quatro classes de exceções de nível de hardware (Interrupções, Traps, Falhas, Abortos) e seus mecanismos de tratamento.
- Gerenciar ciclos de vida de processos usando chamadas de sistema para criação (
fork), recompensa (waitpid) e execução (execve). - Implementar manipuladores de sinal seguros que considerem concorrência, sinais não enfileirados e segurança contra sinais assíncronos.
🔹 Lição 9: Memória Virtual
Visão Geral: Esta lição explora a Memória Virtual (MV) como uma abstração fundamental que fornece a cada processo um espaço de endereçamento grande, contínuo e privado. Cobrimos suas três funções principais: uma ferramenta para cache eficiente na DRAM, um mecanismo de gerenciamento e proteção de memória, e uma base para mapeamento de memória. Além disso, a lição aprofunda os mecanismos de tradução de endereços (TLB), alocação dinâmica de memória (gerenciamento do heap) e os princípios da coleta automática de lixo, concluindo com falhas críticas relacionadas à memória na programação em C.
Resultados de Aprendizagem:
- Distinguir entre endereçamento físico e virtual e descrever o papel da Unidade de Gerenciamento de Memória (MMU).
- Realizar tradução de endereços virtuais para físicos usando Tabelas de Páginas e o Buffer de Tradução de Endereços (TLB).
- Analisar e implementar estratégias de alocação de memória dinâmica, incluindo listas implícitas/explícitas e coalescência.
🔹 Lição 10: Entrada/Saída a Nível de Sistema
Visão Geral: Esta lição explora a interface fundamental entre o sistema operacional Linux e programas de aplicação para realizar entrada e saída. Cobremos as chamadas de sistema Unix básicas, os diversos tipos de arquivos encontrados no sistema de arquivos Linux e as estruturas de dados de nível do kernel usadas para gerenciá-los. Além disso, apresentamos o pacote Robust I/O (RIO) para lidar com "contagens curtas" e fornecemos diretrizes para escolher entre I/O padrão e I/O a nível de sistema em diferentes contextos de programação, como programação de redes.
Resultados de Aprendizagem:
- Implementar operações básicas de arquivos usando a interface Unix I/O (
open,close,read,write). - Diferenciar entre arquivos regulares, diretórios e links enquanto consulta metadados de arquivos usando
stat. - Utilizar o pacote RIO para realizar operações de I/O robustas, com buffer e sem buffer.
🔹 Lição 11: Programação de Redes
Visão Geral: Esta lição explora a arquitetura fundamental de aplicações baseadas em rede, centrada no modelo cliente-servidor e na Internet Global IP. Os alunos aprenderão a navegar pela interface Sockets — a API principal para comunicação de rede a nível de sistema — e progredirão para implementar um servidor web funcional (TINY) capaz de entregar tanto arquivos estáticos quanto conteúdo dinâmico por meio da Interface de Gateway Comum (CGI).
Resultados de Aprendizagem:
- Compreender o ciclo de requisição-resposta do modelo cliente-servidor e a hierarquia de hardware/software da Internet Global IP.
- Manipular e converter endereços IP, nomes de domínio e estruturas de socket usando funções independentes de protocolo como
getaddrinfo. - Implementar um servidor web iterativo robusto e programas CGI que utilizem controle de processos e redirecionamento de I/O para servir conteúdo dinâmico.
🔹 Lição 12: Programação Concorrente
Visão Geral: Esta lição explora os modelos fundamentais de concorrência: processos, multiplexação de I/O e threads. Oferece uma análise aprofundada sobre sincronização usando semáforos para resolver condições de corrida, padrões arquiteturais comuns como produtor-consumidor e servidores pré-threaded, e as métricas usadas para avaliar o desempenho paralelo. Finalmente, aborda questões críticas de confiabilidade, incluindo segurança de thread, reentrância e prevenção de deadlocks.
Resultados de Aprendizagem:
- Distinguir entre modelos de concorrência baseados em processos, multiplexação de I/O e threads.
- Aplicar operações de semáforo (P e V) para garantir exclusão mútua e resolver padrões de sincronização.
- Calcular métricas de desempenho paralelo, como ganho de velocidade e eficiência, sob diferentes leis de escalabilidade.