pbn/curso/aula-01.org

309 lines
12 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+title: 1 -- Arquitetura de computadores
#+author: Blau Araujo
#+email: cursos@blauaraujo.com
#+options: toc:3
* 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.
#+begin_quote
*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.
#+end_quote
** 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)
#+begin_quote
*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.
#+end_quote
* 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.
#+begin_quote
*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.
#+end_quote
** 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.
#+begin_quote
*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.
#+end_quote
** 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 [[exemplos/01/exit42.asm][exit42.asm]]
#+begin_src asm :tangle exemplos/01/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
#+end_src
** Montagem e execução (no terminal)
#+begin_example
:~$ nasm -f elf64 -o exit42.o exit42.asm
:~$ ld -o exit42 exit42.o
:~$ ./exit42
:~$ echo $?
42
#+end_example
* Exercícios sugeridos
1. Modifique o programa Assembly para retornar "sucesso".
1. Por que eu usei o termo "montagem"?
1. Desmonte (=objdump -d=) os binários gerados e compare os códigos de máquina.
1. Use =strace ./exit42= para verificar a chamada de sistema realizada.
* Referências
- [[https://namazso.github.io/x86/][Intel® 64 and IA-32 Instruction Set Reference]]
- [[https://wiki.osdev.org/CPU_Registers_x86-64][OS Dev: CPU Registers x86-64]]
- [[https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/][Linux System Call Table for x86 64]]
- =man 2 exit=