Controle fino.
Ideal para boot, registradores, interrupções e mudanças de privilégio.
mov cr3, rax
Marco 19 · Rede útil
O kernel negocia a configuração, valida os pacotes e oferece duas
syscalls pequenas; NET.ELF torna a prova visível.
saída do SOIA
soia> run net.elf
DHCP 10.0.2.15/24: OK
UDP OFFER + ACK: OK
gateway.soia → 10.0.2.2: OK
TCP SYN → SYN-ACK → ACK: OK
Memoria devolvida: OK.
Ambiente seguro
O QEMU cria um laboratório isolado. O SOIA enxerga recursos virtuais; seu Windows e seu disco físico ficam fora do experimento.
O que entregamos
1 CPU virtual, perfil de 64 MiB ou 2 GiB, tela, teclado, COM1 e a imagem do projeto.
O que não entregamos
Seu HD/SSD físico, arquivos pessoais ou acesso direto ao Windows.
Seu ritmo
Você pode trocar de nível a qualquer momento. O assunto continua o mesmo.
Seção especial · C no SOIA
C++ é outra linguagem e ainda não foi habilitada. Aqui o Clang transforma C em instruções x86_64 que o SOIA consegue carregar.
C é uma forma mais curta e organizada de escrever regras. A CPU continua executando código de máquina; o compilador faz a tradução.
O alvo é x86_64-unknown-none-elf.
Não há Windows, libc, runtime hospedado ou símbolos externos.
Ideal para boot, registradores, interrupções e mudanças de privilégio.
mov cr3, rax
Ideal para strings, buffers, comandos, editores e protocolos.
soia_write("ola", 3);
Exigiria definir runtime, construtores, new/delete, exceções e RTTI.
etapa futura
libsoia é a ponte: organiza registradores e chama
INT 0x80. O aplicativo não toca no hardware.
.datavariável com valor gravado no ELF
.bssvariável que começa zerada
W^Xgravável ou executável; nunca ambos
Comandos, histórico, métricas e arquivos.
Ctrl+S persiste NOTE.TXT no disco virtual.
CPU, RAM, SOIAFS1, ATA, scheduler e rede.
DHCP, UDP, DNS e TCP por syscalls validadas.
Capacidade fixa, escrita e blocos livres.
Marco 11 · Interface gráfica
Antes, a VGA entregava uma grade pronta de caracteres. Agora o SOIA recebe uma tela vazia e decide a cor de cada ponto.
O estágio 2 seleciona VBE, copia a fonte BIOS 8 × 16 para
0x6000 e entrega ao kernel o endereço do framebuffer
XRGB8888 informado pelo ModeInfo.
SOIA Terminal 0.7 - rede e processos em ring 3 F1 Terminal | F2 Notas | F3 Monitor | F4 ultimo app soia> status Status do SOIA: CPU: 1 vCPU x86_64 RAM gerenciada: 2 GiB soia> _
O salto conceitual
Pense no framebuffer como papel quadriculado: para colorir um quadrado, o kernel encontra sua posição e grava o número da cor.
O pixel (x, y) fica em
framebuffer + y × 4.096 + x × 4. O valor
0x00RRGGBB contém diretamente os três canais.
Já funciona
1024 × 768 em janela GTK 1:1,
terminal e notas C, foco por F1/F2/F3, texto 8 × 16, cursor,
Backspace e rolagem.
Custo medido
2,56× mais pixels e cerca de
1,83 MiB adicionais de memória de vídeo.
Marco 13.1 · Monitor do sistema
Terminal, notas e monitor são aplicativos C separados; o kernel conserva as chaves e fornece métricas validadas.
Três ELF executam em CPL 3. O scheduler usa round-robin, SLEEP e HLT; o TSC virtual separa trabalho de tempo ocioso.
A viagem de uma linha
cat até os bytes do disco.SOIA Terminal 0.7 - rede e processos em ring 3
F1 Terminal | F2 Notas | F3 Monitor | F4 ultimo app
soia> echo ola
ola
soia> ticks
ticks: 46
soia> _
Contrato INT 0x80
RAX escolhe o serviço; registradores carregam argumentos e o retorno.
O endereço também é conferido. O shell não pode apontar para qualquer lugar e mandar o kernel escrever.
Leitura: texto, dados, pilha ou heap concedido. Escrita: dados/BSS, pilha e heap RW+NX, nunca acima do break atual.
helpMapa do shellMostra os comandos disponíveis.
clearJanela limpaRedesenha a interface e reposiciona o cursor gráfico.
echo TEXTOTexto de voltaPreserva o trecho digitado depois do espaço.
ticksTempo do kernelConsulta quantos pulsos de 20 Hz ocorreram.
memRAM disponívelConsulta as páginas físicas livres de 4 KiB.
statusResumo honestoMostra CPU pelo TSC virtual, uptime, RAM, SOIAFS1, ATA e tarefas.
lsDiretório raizPercorre as entradas oferecidas pela VFS.
cat ARQUIVOArquivo realLê README.TXT, NOTE.TXT ou outro arquivo da raiz pelo nome.
psTabela de processosMostra PID, estado e nome das vagas ocupadas.
run CALC.ELFNovo processoCria a calculadora, aguarda sua saída e recolhe a memória.
run HEAP.ELFMemória dinâmicaTesta malloc, free, calloc e páginas sob demanda.
run IPC.ELFMensagem isoladaEnvia ping, recebe pong e prova que um handle antigo deixa de valer.
run NET.ELFRede útilMostra DHCP, UDP, DNS e um handshake TCP real no QEMU.
Marco 13.1 · telemetria protegida
SOIA Monitor 0.1
CPU virtual 4%
terminal 0%
notas 0%
monitor 3%
RAM usada 192 KiB
SOIAFS1 105.070 / 2.065.408 B
ATA leituras + escritas · 0 erros
rede RTL8139 DHCP 1 · UDP 2 · DNS 2 · TCP 2
Teste comportamental
O teste injeta comandos e percorre dez posições com as setas. O resultado só aparece se IRQ1, fila, tarefa, syscall, terminal, VFS e ATA cooperarem.
soia> ls
README.TXT
soia> cat readme.txt
Ola do SOIAFS: bytes persistentes no disco virtual.
Limite honesto
Capítulo zero · A base
Antes de estudar kernel, precisamos separar três perguntas: qual é o valor, onde ele está e o que ele significa.
01 + 02 · Bit e byte
Cada bit é uma escolha entre 0 e 1.
Ligue alguns interruptores; os pesos ligados são somados.
Um byte possui 2⁸ = 256 combinações. O bit mais à
direita vale 2⁰; o mais à esquerda vale
2⁷.
Um byte nem sempre viaja bit por bit. A COM1 usa start e stop; o ATA PIO entrega 16 bits por leitura.
A COM1 atual usa 38.400 baud em 8N1: 26,04 µs por bit e 260,42 µs por quadro. Offset localiza bytes já armazenados; não delimita o sinal.
Comparar tempo e enquadramento no PlaygroundPesos ligados: 64 + 16 + 4 + 1 = 85
O U só aparece porque ASCII interpreta
0x55 como texto. Em uma instrução, o mesmo byte teria
outro significado.
03 · Hexadecimal
Hexadecimal conta com dez números e seis letras. Cada casa vai de
0 a F; depois de 0F vem
10.
08090A0B
0C0D0E0F
vai um →10
00 até FF
256 valores possíveis · máximo decimal 255
01015
+
01015
= 0x55
04 · A rua da memória
A base é o número da porteira do terreno; o offset diz quantas casas andar dentro dele. Somando os dois, chegamos ao endereço.
Neste exemplo, segmentos estão zerados e ORG 0x7C00
corresponde à carga da BIOS. Em modo real, a regra geral é
segmento × 16 + deslocamento.
05 · Primeiro setor
A BIOS copia o primeiro setor para 0x7C00. Os
offsets começam em zero, então o último byte fica em
0x1FF, não em 0x200.
0x7C00
assinatura0x7DFE e 0x7DFF
55 AA não é o texto “55 AA”.
São dois valores binários usados pela BIOS como uma marca de setor
inicializável.
06 · CPU e significado
A CPU moderna inicia em um estado compatível com PCs antigos. Nosso carregador prepara gradualmente os ambientes de 32 e 64 bits antes de entregar o controle ao kernel.
Ela começa com regras compatíveis com o 8086 e precisa ser preparada para usar os modos modernos.
É principalmente o tamanho padrão de operandos e registradores. Instruções x86 têm tamanho variável.
O modo real usa segmento × 16 + deslocamento; a linha A20 acrescenta uma nuance no limite.
01010101
→
como número85
como texto ASCIIU
como opcode x86push bp/ebp/rbp
Próxima volta do ciclo
O laboratório mostra offset, hexadecimal, texto e instrução nos arquivos que realmente inicializam o SOIA.
Marcos 09 + 15 · Disco e persistência
O disco é o depósito, o driver busca caixas, o sistema de arquivos organiza e a VFS oferece um catálogo comum.
O kernel usa ATA PIO com LBA28, valida o SOIAFS1 v2,
consulta o bitmap e grava dados e metadados com flush.
Capacidade fixa
2.097.152 bytesdisco virtual de 2 MiB
A distinção que evita a confusão
Setor organiza bytes persistentes no disco. Endereço identifica onde cada byte será colocado na RAM durante a execução.
44 × 512 = 22.528 B
DHCP + UDP + DNS + TCP
kernel.bin · 28.672 B
Os doze setores acomodam configuração DHCP, parsers, checksums, buffers e contratos de rede. O último setor continua preenchido até fechar sua caixa de 512 bytes.
0x100000x16FFFinício fixo28.672 bytes consecutivosnovo fim
O começo continua em 0x10000. Apenas o fim passa
de 0x157FF para 0x16FFF. Aqui cada
endereço nomeia um byte; o kernel também gerencia a RAM em
páginas de 4 KiB.
O disco ganhou quatro caixas de kernel; na RAM, o mesmo terreno inicial recebeu 6.144 bytes a mais no final.
A ficha que permite reconhecer e validar o volume antes de confiar nele.
0x000x080x0C0x100x140x30Entradas associam nomes aos dados; um bit marca cada setor livre ou ocupado.
O transporte
O kernel informa a caixa, espera o disco e transporta um setor entre a imagem e a RAM.
0x20 + REP INSW leem; 0x30
+ REP OUTSW escrevem; 0xE7 solicita flush.
A abstração
A VFS recebe um nome. O driver do formato localiza a entrada; o driver de bloco busca os bytes.
vfs_read_file("README.TXT")
└─ soiafs_lookup()
└─ ata_read_sector(40)
Evidência no QEMU
O teste salva NOTE.TXT, fecha o primeiro QEMU e
exige o texto ao iniciar outro com a mesma imagem.
“NOTE.TXT carregado do disco virtual.”
Gravável, com uma fronteira pequena e explícita.
Somente NOTE.TXT, com até 2.047 bytes, pode ser salvo.
A imagem limpa começa com 215 usados · 3.819 livres no
volume. Ainda não existem exclusão, diretórios, journal ou recuperação.
Fundamentos do núcleo
Estes marcos sustentam o terminal atual. O seletor evita repetir toda a história na página; a documentação Markdown conserva o desenvolvimento completo.
A syscall atual pertence ao Marco 10; aqui, Processos explica apenas o isolamento e a troca de tarefas que a tornam possível.
Marco 08 · Isolamento
Cada programa vive em seu próprio apartamento. O número da sala pode ser igual; a planta escolhida pela CPU leva a lugares físicos diferentes.
Cada tarefa possui CR3, tabelas, código e pilhas próprios. PTEs de usuário usam U/S=1; as folhas do kernel permanecem U/S=0.
Autoridade em camadas
A CPU impede que programas comuns executem instruções privilegiadas ou escrevam nas páginas do kernel.
Tradução por tarefa
RIP 0x100000000
O mesmo número não significa o mesmo lugar depois da tradução.
Preempção a 20 Hz
Isolamento, CR3 e scheduler ficam aqui.
O contrato atual de INT 0x80, suas vinte e quatro syscalls e a
prova interativa pertencem ao Terminal.
Marco 07 · Gerenciamento
A BIOS entrega uma planta. O kernel abre caminhos somente até a memória existente e distribui quartos de 4 KiB sem colisões.
E820 descreve intervalos físicos; 1.024 PDEs de 2 MiB ampliam o mapa e um bitmap controla 524.288 quadros de 4 KiB.
Mapa recebido da BIOS
Esquema didático: o mapa real contém várias entradas e pode ter pequenos “buracos” reservados.
quais regiões existem?
onde está o mapa?
até 2 GiB identidade
livre ou ocupada?
Dois tipos de endereço
0x020000000x02000000Hoje os números são iguais: isso é um mapeamento identidade. A tradução ainda existe.
Um bit por página
1 não pode ser entregue; 0 está livre para alocação.
Autoteste real no boot
A máquina ganhou campainhas
Em vez de perguntar sem parar se algo aconteceu, o kernel dorme. Timer e teclado tocam a campainha quando precisam de atenção.
IRQ0 e IRQ1 passam pelo PIC remapeado, chegam aos vetores
0x20 e 0x21 da IDT e retornam com
IRETQ.
IDT na prática
base 0x20000 · limite 4095
Dentro de uma entrada
0x8E
O endereço da rotina é repartido em três pedaços porque o formato da porta evoluiu junto com a família x86.
O espaço de 8 bits
O vetor é apenas um número de 8 bits usado como índice:
0 a 255.
Viagem completa da tecla A
Tecladolevanta IRQ 1
PICentrega vetor 0x21
CPUsalva RIP, CS e RFLAGS
IDT[33]fornece o endereço da ISR
ISRlê 0x60 e envia EOI
IRETQrestaura e continua
IDT, vetores e a viagem da tecla ficam aqui. A fila de teclado, edição de linha e interpretação do comando estão reunidas no Terminal.
Detalhes do Marco 6A primeira entrega
O bootloader encontra o kernel, prepara a máquina, entrega uma ficha com o ambiente disponível e sai de cena.
O estágio 2 lê 56 setores por INT 13h/AH=42h em
0x10000, obtém o mapa E820, prepara VBE, entra em
long mode e salta com RAX=SOIABOOT e
RDI=&boot_info.
O segundo estágio busca o novo programa nos setores seguintes.
INT 13h/AH=42h lê os LBAs 6–61 em 1000:0000.
O carregador monta o ambiente antes de chamar o núcleo.
A20, GDT, paginação e long mode permanecem responsabilidades do bootloader.
Uma ficha informa memória, tela, serial e endereço do kernel.
A versão 4 descreve VBE, framebuffer, pitch, profundidade, fonte e conserva o mapa E820.
Depois de conferir a ficha, ele limpa a tela e usa seus próprios recursos.
A entrada em 0x10000 valida RAX/RDI e não retorna ao bootloader.
Disco, RAM e CPU
soia.img contém carregador, kernel e SOIAFS1. Código é
carregado na RAM; o volume continua no disco até o kernel pedir
seus setores.
O arquivo soia.img reúne quatro blocos persistentes
que precisam existir antes de a máquina ligar.
O kernel abre o conteúdo que ficou depois dele no disco.
O carregador copia o kernel e cria estruturas temporárias em endereços distintos do computador virtual:
0x1000PML40x2000PDPT0x3000Diretório0x5000Mapa E8200x6000Fonte BIOS 8 × 160x7000VBE ModeInfo0x8000Estágio 20x10000Kernel0x20000IDT0x30000Bitmap · 64 KiB0x100000000Tarefas0x90000PilhaVBEFramebuffer MMIOBootloader e kernel coexistem na RAM, mas têm funções separadas.
A CPU continua em 64 bits; o salto para 0x10000
entrega a execução a outro binário.
O disco guarda código e arquivos. A RAM recebe
o código no boot e os setores do volume quando o kernel os lê.
A CPU troca o dono da execução no salto para 0x10000.
Laboratório hexadecimal
Arquivos .bin não são texto: cada posição contém um
número de 0 a 255. Aqui você pode abrir os bytes reais do SOIA sem
precisar saber binário previamente.
A unidade
FA vira oito bits?
Um dígito hexadecimal representa quatro bits. Por isso dois
dígitos, de 00 a FF, representam um byte.
Seu primeiro comando
Format-Hex.00000000
FA 31 C0 8E D8 8E C0 8E
.1......
1Offsetposição dentro do arquivo
2Bytesvalores em hexadecimal
3Textotentativa de ler como caracteres
Um ponto significa “este byte não forma um caractere imprimível”. Isso não é erro: provavelmente é instrução ou dado.
Seu segundo comando
ndisasm.00007C00
FA
cli
1Endereçoonde a CPU vê a instrução
2Byteo número armazenado
3Assemblytradução para humanos
Nesse contexto, FA significa cli:
desligar interrupções enquanto a pilha é preparada.
Bytes reais do build
Compile o projeto com .\scripts\Build.ps1 para gerar
os dados deste visualizador.
Marcos 14 + 19 · Rede básica e útil
O kernel encontra a placa, negocia DHCP, resolve um nome e completa
TCP; NET.ELF vê resultados, nunca portas ou buffers DMA.
Analogia
Ethernet é o envelope; DHCP entrega a configuração; DNS acha um endereço; TCP confirma que os dois lados podem conversar.
Negociação real
O endereço não é mais constante: DHCP entrega
10.0.2.15/24, gateway .2 e DNS .3.
UDP 68 → 67. O cliente transmite DISCOVER e REQUEST em broadcast.
Opções 1, 3 e 6. Máscara, gateway e DNS vêm do ACK aceito.
Validação. XID, magic cookie, portas, tipo DHCP e checksum IPv4 precisam coincidir.
Nome seguro e reproduzível
gateway.soia vira 10.0.2.2.A syscall 23 copia até 63 bytes para o kernel. O registro local usa o gateway recebido; o cliente por UDP continua disponível para consultas A externas.
Determinístico. O teste não depende de internet nem do resolver do host.
Formato real. O caminho por fio monta QNAME, valida a resposta e percorre nomes comprimidos.
Limite honesto. A prova automática não afirma resolução externa quando o sandbox bloqueia a resposta.
Handshake mínimo
guestfwd liga 10.0.2.100:8080 a um listener temporário em localhost. A syscall 24 repete a prova para NET.ELF.
Ainda não existem sockets genéricos, payload TCP, retransmissão, FIN, HTTP, TLS ou IPv6.
No fio virtual
qemu-network.pcap
ARP 6 quadros
IPv4 14 quadros
DHCP 4 mensagens
TCP 2 handshakes
Na userland
run NET.ELF.DHCP + UDP OK
gateway.soia OK
TCP :8080 OK
memória devolvida OK
Marco 16 · processos dinâmicos
O kernel prepara um espaço isolado para o programa e limpa tudo quando ele sai. As syscalls 15–18 implementam spawn, exit, wait e leitura validada da tabela de processos.
Experimento útil
run CALC.ELFA calculadora aceita inteiros, + - * /, trata divisão por zero e encerra com sair.
Ciclo do pai. spawn + wait cria o filho e bloqueia o terminal sem polling.
Tabela de oito. Três vagas residentes e cinco vagas dinâmicas.
Estados reais. Livre, pronto, dormindo, aguardando e finalizado.
Sem vazamento. O teste exige a mesma contagem de páginas antes e depois.
soia> ps
PID ESTADO PROGRAMA
1 executando TERMINAL.ELF
2 dormindo NOTEPAD.ELF
3 dormindo MONITOR.ELF
soia> run calc.elf
Processo iniciado; PID 4.
calc> -7 + 2
resultado: -5
exit primeiro marca o filho como zombie, pois sua pilha
de kernel ainda está em uso. Depois da troca de contexto, o pai
recolhe CR3, sete páginas de tabelas, oito de userland e uma pilha
de kernel. O mesmo loader valida CALC.ELF e continua
rejeitando deliberadamente BROKEN.ELF.
Marco 17 · heap + libc mínima
O aplicativo ganha uma bancada privada, reaproveita espaços livres e devolve tudo ao fechar. A syscall 19 mapeia até 16 páginas RW+NX no PT de usuário; first-fit administra blocos alinhados em 16 bytes.
Experimento verificado
run HEAP.ELFTrês alocações atravessam duas páginas, um bloco liberado volta no mesmo endereço e a coleta encerra sem vazamento.
Base fixa. O heap começa vazio em 0x100008000.
Limite honesto. São no máximo 16 páginas, ou 64 KiB por processo.
W^X preservado. Dados dinâmicos são graváveis e nunca executáveis.
Camada essencial
Pequenos blocos vizinhos são unidos. As páginas continuam no processo para evitar trabalho repetido e voltam ao kernel no exit.
Camada técnica
base 0x100008000
break base + 6.288 B
mapa 2 × 4 KiB
limite 0x100018000
soia> run heap.elf
primeiro bloco: 0x0000000100008020
buffer dinamico via WRITE: buffer do heap aceito pela syscall WRITE
bloco liberado reutilizado no mesmo endereco: OK
calloc zerou 128 bytes: OK
paginas fisicas mapeadas sob demanda: 2
Memoria devolvida: OK.
Se uma alocação física falhar, BRK desfaz somente as PTEs
criadas naquela chamada. INVLPG invalida o TLB e a
validação das outras syscalls usa o break atual como limite.
Marco 18 · IPC + serviços
Cada processo tem uma caixa postal privada. O kernel confere o destinatário, copia a mensagem e acorda quem estava esperando. Cada tarefa possui uma fila circular de duas mensagens de até 48 bytes; handles codificam geração e PID para rejeitar identidades obsoletas.
Experimento verificado
run IPC.ELFO terminal envia ping, bloqueia sem consumir CPU e volta a executar quando o filho responde pong.
Identidade temporal. O handle combina geração e PID; reutilizar a vaga não ressuscita uma referência antiga.
Cópia controlada. O remetente não recebe acesso ao CR3 nem aos ponteiros do destinatário.
Espera eficiente. RECEIVE vazio muda a tarefa para RECEIVING; o timer executa outra tarefa.
Camada essencial
O PID diz qual vaga procurar; a geração confirma que ainda é a mesma vida do processo. Ao recolher o filho, o kernel troca a geração.
Camada técnica
handle = geração << 8 | PID
fila = 2 mensagens
dados = até 48 bytes
struct = 64 bytes
soia> run ipc.elf
Processo iniciado; PID 4; handle 772.
ping enviado ao filho: OK
RECEIVE bloqueou sem consumir CPU: OK
pong recebido do filho: OK
conteudo da resposta: OK
handle do remetente: OK
handle antigo rejeitado: OK
Memoria devolvida: OK.
As syscalls 20, 21 e 22 abrem um handle, enviam e recebem. Ao entregar
diretamente para um receptor bloqueado, o kernel desabilita interrupções,
troca temporariamente para o CR3 do receptor, valida e copia o resultado,
grava o retorno salvo em RAX e restaura o CR3 do remetente.
Ainda não existem nomes de serviço, permissões por handle nem mensagens maiores.
Roteiro
A IA ajuda a construir este projeto, mas não executa dentro dele. O roteiro agora segue os subsistemas clássicos de um SO completo.
Concluído · Marco 19
DHCP configura o guest; DNS resolve um nome; TCP confirma o
handshake; NET.ELF enxerga apenas syscalls e métricas.
Configuração real. DISCOVER/OFFER/REQUEST/ACK aparecem no PCAP.
Isolamento. PCI, RTL8139 e DMA continuam exclusivos do ring 0.
Prova obrigatória. O QEMU confirma DNS local e dois handshakes TCP.
Ferramenta de desenvolvimento
Ela apoia arquitetura, código, revisão, testes e documentação. A imagem final funciona sem modelo, conta, API ou conexão de IA.
Vocabulário
Abra um conceito quando precisar. O glossário completo permanece na documentação técnica.
Ver glossário em MarkdownSimples: a autoridade que permanece administrando o computador.
Técnico: código em ring 0 que controla memória, hardware e serviços.
Simples: a área limitada em que programas comuns executam.
Técnico: CPL 3, sem acesso a páginas supervisoras ou instruções privilegiadas.
Simples: um pedido controlado que um programa faz ao kernel.
Técnico: travessia validada por INT 0x80, com número e argumentos em registradores.
Simples: a área que mostra texto e permite editar uma linha.
Técnico: driver de console em janela gráfica, com cursor, rolagem e entrada enfileirada.
Simples: o programa que entende o comando digitado.
Técnico: tarefa em ring 3 que analisa comandos e usa syscalls para pedir serviços.
Simples: uma porta comum para abrir arquivos sem conhecer o disco por baixo.
Técnico: interface que traduz operações genéricas para o formato SOIAFS1.
Fontes deste caderno
O Markdown guarda o detalhe completo. Este caderno organiza o mesmo conhecimento para revisão e estudo.