pbn/curso/aula-01.org

12 KiB
Raw Blame History

1 Arquitetura de computadores

Objetivos

  • Compreender os principais componentes de um computador sob o modelo de von Neumann.
  • Reconhecer os registradores da arquitetura x86_64.
  • Entender a relação entre hardware e código Assembly.
  • Executar o primeiro programa Assembly com uma chamada de sistema.

Modelo de von Neumann

Um computador possui:

  • Unidade de processamento (ALU + Controladora)
  • Memória (armazenamento de instruções e dados)
  • Dispositivos de entrada/saída

O modelo de Von Neumann é uma arquitetura de computadores em que a unidade central de processamento (CPU) e a memória compartilham um único espaço de armazenamento, tanto para dados quanto para instruções de programa. Isso significa que o processador acessa a memória de forma sequencial para buscar dados e instruções, utilizando um único barramento para ambas as operações.

No modelo de Von Neumann:

  • Instruções e dados compartilham o mesmo espaço de memória.
  • A CPU executa o ciclo: busca → decodifica → executa.

Como era antes:

  • Instruções e dados armazenados em memórias separadas.
  • A CPU executa o ciclo: busca → decodifica → executa, mas pode realizar a busca de dados e instruções em paralelo.

Influência nas arquiteturas modernas

O modelo de Von Neumann influenciou profundamente as arquiteturas modernas de computadores, estabelecendo a base para a maioria dos designs de sistemas computacionais atuais. Algumas das principais influências incluem:

Memória unificada
A ideia de compartilhar a mesma memória para dados e instruções se manteve como o padrão em muitas arquiteturas modernas, simplificando o design dos sistemas, embora existam variações: como a memória cache, que separa dados e instruções em níveis mais próximos ao processador.
Ciclo de busca, decodificação e execução
O modelo de Von Neumann introduziu o ciclo básico de execução de instruções, que ainda é fundamental em CPUs modernas. As arquiteturas atuais, como x86 e ARM, seguem esse ciclo de maneira semelhante, embora com otimizações como a pipelining, onde múltiplas fases do ciclo podem ser realizadas em paralelo.
Programação e flexibilidade
O modelo permite que os programas sejam tratados como dados, o que possibilita a criação de sistemas que podem ser facilmente modificados e adaptados. Isso contribui para o desenvolvimento de linguagens de programação de alto nível, sistemas operacionais e aplicativos dinâmicos.
Simplicidade e custo
O modelo de Von Neumann simplificou o design de computadores ao integrar a memória de instruções e dados. Isso contribuiu para a redução de custos de hardware, tornando os sistemas mais acessíveis e facilitando o desenvolvimento de sistemas comerciais e pessoais.

Nota: A pipelining (do conceito de "linha de montagem") é uma técnica de execução paralela em que múltiplas instruções são processadas simultaneamente em diferentes estágios do ciclo de execução para aumentar o desempenho da CPU. Isso difere do conceito geral de paralelismo, onde tarefas completas, e não estágios do processamento, são executadas simultaneamente.

Gargalo de Von Neumann

O gargalo de Von Neumann é uma limitação de desempenho causada pelo fato de que a CPU e a memória compartilham o mesmo barramento para acessar instruções e dados.

Isso significa que:

  • A CPU não pode buscar uma instrução e acessar dados ao mesmo tempo.
  • O tempo de espera entre operações aumenta, especialmente em programas que exigem muitos acessos à memória.
  • A largura de banda do barramento se torna um fator crítico para o desempenho.

Esse gargalo levou ao desenvolvimento de soluções como:

  • Memória cache (para reduzir acessos à RAM)
  • Execução paralela e pipelines
  • Arquiteturas modificadas (como o modelo Harvard modificado)

Nota: A arquitetura Harvard modificada é uma variação do modelo de Von Neumann que usa memórias separadas internamente (como em caches) para dados e instruções, mantendo um espaço de memória unificado do ponto de vista do programador.

Arquiteturas x86

A arquitetura x86 é uma família de conjuntos de instruções (ISA Instruction Set Architecture) baseada no modelo de Von Neumann e desenvolvida originalmente pela Intel a partir do processador 8086.

Assim como no modelo de Von Neumann:

  • Dados e instruções compartilham o mesmo espaço de memória.
  • A CPU segue o ciclo: busca → decodifica → executa.

Nota: A transição da arquitetura x86 de 32 para 64 bits foi liderada pela AMD com a criação da AMD64 em 2003. Essa extensão manteve compatibilidade com o conjunto de instruções x86 original (IA-32), mas adicionou registradores de 64 bits e suporte a endereçamento ampliado. Posteriormente, a Intel adotou essa mesma arquitetura sob o nome Intel 64 (anteriormente chamada EM64T), e o termo x86-64 passou a ser usado de forma genérica para se referir à arquitetura compatível com AMD64. Assim, AMD64 é a origem técnica da arquitetura x86 de 64 bits utilizada na maioria dos sistemas modernos.

Características

  • ISA complexa (CISC Complex Instruction Set Computing), com centenas de instruções e modos de endereçamento.
  • Suporte a múltiplos tamanhos de palavra (16, 32 e 64 bits nas versões modernas).
  • Registradores de uso geral (AX, BX, CX, etc.) e segmentados (CS, DS, SS…, não utilizados no Linux), herdados de versões mais antigas.
  • Ampla compatibilidade com versões anteriores (retrocompatibilidade).
  • Utilizada em desktops, laptops e servidores, sendo a base da maioria dos PCs atuais.

Nota: Embora a arquitetura x86 inclua registradores segmentados (como CS, DS, ES, SS), o Linux não faz uso da segmentação de memória no modo protegido. Em vez disso, ele utiliza o chamado endereçamento plano, tratando toda a memória como um único espaço contínuo. A segmentação é mantida apenas em um nível mínimo para atender exigências da arquitetura (como troca de contexto e proteção básica), mas a segmentação lógica, como era usada no MS-DOS, é totalmente evitada.

Otimizações em relação ao modelo de Von Neumann

A arquitetura x86 é uma evolução prática do modelo de Von Neumann, com otimizações como:

  • Uso extensivo de memória cache.
  • Execução fora de ordem (out-of-order execution).
  • Pipelines e paralelismo interno.

Comparativo com outras arquiteturas

Todas as arquiteturas modernas seguem, em maior ou menor grau, os princípios do modelo de Von Neumann. No entanto, diferem na forma como organizam e executam instruções. Veja o comparativo:

x86 (CISC Complex Instruction Set Computing):

  • Conjunto de instruções extenso e complexo.
  • Instruções de vários tamanhos e com múltiplos modos de endereçamento.
  • Maior consumo de energia, mas com alta compatibilidade e maior desempenho bruto.
  • Retrocompatibilidade com código legado.
  • Comum em PCs, laptops e servidores.

ARM (RISC Reduced Instruction Set Computing)

  • Conjunto de instruções reduzido e regular.
  • Foco em simplicidade, baixa energia e eficiência.
  • Desempenho por watt muito superior ao x86.
  • Predominante em dispositivos móveis (smartphones, tablets) e embarcados.
  • ARM64 (AArch64) é a versão de 64 bits moderna.

RISC-V (RISC e open source):

  • ISA aberta, modular e extensível.
  • Sem royalties: qualquer um pode implementar.
  • Design limpo e simples, adequado para pesquisa, educação e sistemas customizados.
  • Crescimento em sistemas embarcados e processadores personalizados.

Resumo comparativo:

Arquitetura Tipo Complexidade Consumo Uso comum
x86 CISC Alta Alto PCs, laptops, servidores
ARM RISC Média Baixo Celulares, IoT, Apple M1+
RISC-V RISC Baixa Baixo Pesquisa, sistemas embarcados

Componentes de uma CPU x86_64

Unidade de Controle (Control Unit - CU)
Gerencia o fluxo de dados e as instruções dentro da CPU, coordenando as operações de execução.
Unidade Lógica e Aritmética (ALU Arithmetic and Logic Unit)
Responsável pela execução de operações aritméticas (soma, subtração, etc.) e lógicas (AND, OR, NOT, etc.).
Registradores de uso geral
Utilizados para armazenar dados temporários durante a execução de instruções (ex.: RAX, RBX, RCX, etc.).
Registradores de propósito específico
Como o ponteiro de pilha (RSP), ponteiro de instrução (RIP) e flags (como EFLAGS).
Registradores de segmentos
Armazenam endereços de segmentos de memória para código (CS), dados (DS) e pilha (SS), além dos registradores de segmentos adicionais para dados (ES, FS e GS).
Cache
Memória de acesso ultrarrápido usada para armazenar dados frequentemente acessados, visando reduzir o tempo de acesso à memória principal. Normalmente dividida em L1, L2 e, em algumas CPUs, L3.
Barramentos
Conjunto de trilhas de comunicação que transportam dados entre a CPU e outros componentes, como a memória, dispositivos de entrada/saída e outros núcleos.
Unidade de Execução (Execution Unit EU)
Responsável por executar as instruções. Em CPUs modernas, pode haver múltiplas unidades de execução para executar diferentes tipos de operações em paralelo.
Decodificador de Instruções
Interpreta as instruções da linguagem de máquina (bytecode) e as converte para operações que podem ser executadas pela ALU ou outras unidades de execução.
Unidade de carga e armazenamento (Load-Store Unit - LSU)
Controla o carregamento e armazenamento de dados na memória, realizando operações de leitura e escrita.
Unidade de Endereçamento (Address Generation Unit - AGU)
Calcula endereços de memória, especialmente no contexto de operações de acesso à memória e cálculo de ponteiros.
Controlador de Interrupções (Interrupt Controller)
Responsável por lidar com interrupções externas e internas, gerenciando a prioridade e o tratamento adequado das interrupções no sistema.

Principais registradores e seus propósitos (64 bits)

  • RAX: acumulador/propósito geral
  • RBX: base/propósito geral
  • RCX: contador/propósito geral
  • RDX: dados/propósito geral
  • RSI: índice de origem
  • RDI: índice de destino
  • RSP: ponteiro da pilha
  • RBP: base da pilha
  • RIP: ponteiro de instrução
  • RFLAGS: sinalizações diversas

A arquitetura x86_64 ainda inclui oito registradores de propósito geral, de R8 a R15.

Primeiro exemplo em Assembly x86_64

Arquivo exit42.asm

; Retorna 42 como estado de término

section .text
    global _start

_start:
    mov     rax, 60        ; syscall: exit
    mov     rdi, 42        ; código de saída
    syscall

Montagem e execução (no terminal)

:~$ nasm -f elf64 -o exit42.o exit42.asm
:~$ ld -o exit42 exit42.o
:~$ ./exit42
:~$ echo $?
42

Exercícios sugeridos

  1. Modifique o programa Assembly para retornar "sucesso".
  2. Por que eu usei o termo "montagem"?
  3. Desmonte (objdump -d) os binários gerados e compare os códigos de máquina.
  4. Use strace ./exit42 para verificar a chamada de sistema realizada.