Pular para o conteúdo
blog.kristyan.dev
sistemas-operacionais virtualizacao cloud infraestrutura linux seguranca

Virtualização: Tanenbaum é um louco genial

Uma síntese técnica sobre virtualização, hipervisores, memória, E/S, nuvem e VMware a partir de Sistemas Operacionais Modernos 4a. Edição.


Virtualização é uma daquelas ideias que parecem simples até você tentar explicar direito.

Na versão inocente, é “rodar um sistema operacional dentro de outro”. Na versão um pouco menos enganosa, é “criar uma máquina falsa para um sistema operacional acreditar que manda em alguma coisa”. Na versão Tanenbaum, que é a versão que começa civilizada e termina com você olhando para a MMU como quem encara um bicho mitológico, virtualização é uma camada de abstração que precisa enganar CPU, memória, dispositivos de E/S, sistemas operacionais inteiros e, se possível, o usuário também.

E o pior é que funciona.

O capítulo 7 de Sistemas Operacionais Modernos é uma síntese muito boa porque não trata virtualização como buzzword de cloud, nem como botão mágico do VirtualBox. Ele começa na história, passa por exigências formais, entra em hipervisores, cai no buraco da memória virtual, sofre com E/S, fala de nuvem, licenciamento, migração de máquina virtual e fecha com VMware, que é basicamente o estudo de caso de como transformar uma arquitetura inconveniente em produto bilionário.

Este post é uma síntese desse trecho. Não é uma substituição do livro, até porque Tanenbaum tem aquela habilidade irritante de explicar um conceito simples, depois revelar que ele só era simples porque você ainda não tinha visto as três camadas escondidas embaixo.

Virtualização é uma mentira com contrato

A melhor forma de entender virtualização é pensar nela como uma mentira disciplinada.

Uma máquina virtual recebe CPU virtual, memória virtual, disco virtual, placa de rede virtual, BIOS ou UEFI virtual, interrupções virtuais e, em muitos casos, até dispositivos antigos fingindo estar vivos só para o sistema convidado reconhecer alguma coisa. O sistema operacional dentro da VM olha ao redor e pensa: “essa máquina é minha”.

Não é.

Quem manda de verdade é o hipervisor. Ele decide quando a VM executa, quanta memória ela recebe, quais operações são permitidas, quais precisam ser interceptadas e quais serão emuladas. A VM vive numa espécie de teatro computacional, só que com menos aplausos e mais tabela de páginas.

A mentira organizada da virtualização

Essa mentira precisa obedecer a três propriedades clássicas, vindas do trabalho de Popek e Goldberg: equivalência, controle de recursos e eficiência.

Equivalência significa que o software dentro da VM deve se comportar como se estivesse rodando numa máquina real, com exceções aceitáveis como diferenças de tempo. Controle de recursos significa que o hipervisor precisa continuar no comando. Eficiência significa que a maior parte das instruções deve rodar diretamente no hardware, sem o hipervisor ficar metendo o bedelho em tudo.

Essa última parte é importante. Se toda instrução precisasse ser interpretada pelo hipervisor, virtualização seria basicamente uma reunião de condomínio: tudo anda, mas num ritmo que faz você questionar suas escolhas de vida.

A história é mais velha que o hype

Virtualização não nasceu com cloud, Docker, Kubernetes, dev local bonitinho ou aquele tutorial que manda subir uma VM Ubuntu porque “é mais seguro”. A ideia vem dos mainframes.

Nos anos 1960 e 1970, a IBM já trabalhava com sistemas como CP-40, CP-67 e VM/370. O problema era prático: máquinas grandes eram caras demais para ficarem presas a um único usuário ou carga de trabalho. A virtualização permitia dividir uma máquina física poderosa em várias máquinas aparentes.

Ou seja, antes de virar produto de nuvem, virtualização era uma resposta econômica para hardware caro. A nuvem moderna apenas pegou essa lógica, colocou API, billing por hora, IAM, dashboard, região, zona de disponibilidade e uma fatura que chega sorrindo no cartão.

A ideia central continuou parecida: transformar recursos físicos compartilhados em recursos virtuais aparentemente dedicados.

A parte engraçada é que muita gente fala de cloud como se fosse uma ruptura filosófica completa. Em alguns aspectos, é só o mainframe voltando de jaqueta nova, usando JSON e cobrando por tráfego de saída.

O que torna uma arquitetura virtualizável

O livro entra num ponto essencial: nem toda arquitetura é igualmente fácil de virtualizar.

Para uma CPU ser virtualizável de forma elegante, instruções sensíveis precisam causar uma armadilha quando executadas sem privilégio. Instruções sensíveis são aquelas que leem ou alteram estado privilegiado da máquina. Instruções privilegiadas são aquelas que só podem ser executadas no modo mais poderoso da CPU.

O modelo ideal é simples:

  1. o sistema convidado executa normalmente;
  2. quando tenta fazer algo perigoso, a CPU gera uma exceção;
  3. o hipervisor assume;
  4. o hipervisor simula o efeito correto;
  5. a VM continua, fingindo que nada aconteceu.

Esse modelo é chamado de trap and emulate. Bonito, limpo, quase pedagógico. O tipo de coisa que faz parecer que engenharia de sistemas é uma área habitada por pessoas razoáveis.

Aí entra o x86 antigo e estraga a festa.

O problema do x86 clássico era que algumas instruções sensíveis não geravam trap quando executadas sem privilégio. Algumas simplesmente falhavam silenciosamente ou se comportavam de forma diferente. Para um programa comum, isso já seria desagradável. Para um hipervisor, é uma pegadinha arquitetural.

O hipervisor precisa saber quando o convidado tentou fazer algo que afeta o estado da máquina. Se a CPU não avisa, o convidado pode perceber que não está numa máquina real ou, pior, alterar expectativas internas sem o hipervisor conseguir manter a ilusão.

A síntese grosseira é esta: o x86 não era “invirtualizável” porque era impossível. Era invirtualizável do jeito bonito.

E, naturalmente, a indústria olhou para isso e respondeu com a solução mais computacional possível: mais camadas.

Hipervisores tipo 1 e tipo 2

O Tanenbaum separa hipervisores em dois tipos principais.

Hipervisor tipo 1 roda diretamente sobre o hardware. Ele é a camada privilegiada principal da máquina. Controla CPU, memória e dispositivos, e hospeda os sistemas convidados acima dele. Exemplos clássicos incluem VMware ESX/ESXi, Xen e Hyper-V.

Hipervisor tipo 2 roda sobre um sistema operacional hospedeiro. Nesse modelo, existe um sistema operacional normal controlando a máquina física, e o hipervisor funciona como uma aplicação especial acima dele. Exemplos tradicionais incluem VMware Workstation, VirtualBox, Parallels e QEMU em alguns usos.

Hipervisor tipo 1 e tipo 2

A classificação é útil, mas não deve virar religião.

KVM, por exemplo, embaralha a conversa. Ele transforma o Linux em uma infraestrutura de virtualização usando suporte do kernel e extensões de hardware. QEMU pode fornecer emulação de dispositivos e orquestração em espaço de usuário. libvirt pode gerenciar a bagunça. Virt-manager pode colocar uma interface simpática por cima, porque em algum momento alguém precisa clicar em alguma coisa sem decorar quinze flags.

Então sim, tipo 1 e tipo 2 são bons conceitos. Mas, na prática, virtualização moderna frequentemente é uma pilha de componentes cooperando. O mundo real raramente respeita as caixinhas perfeitas dos slides. É uma falta de educação, mas acontece bastante.

Trap, emulação, tradução binária e paravirtualização

Quando o hardware ajuda, o hipervisor pode deixar o sistema convidado executar diretamente e interceptar apenas operações sensíveis. Esse é o mundo bonito do trap and emulate.

Quando o hardware não ajuda, você precisa ser mais criativo. Foi aí que técnicas como tradução binária ficaram importantes.

Na tradução binária, o hipervisor analisa blocos de código do sistema convidado e substitui instruções problemáticas por sequências seguras. Em vez de esperar a CPU gerar uma exceção que talvez nunca venha, o hipervisor reescreve partes perigosas antes que elas causem problemas.

É uma ideia meio absurda e brilhante: se o processador não entrega a armadilha que você precisa, você modifica o caminho antes dele chegar lá. É aquele tipo de engenharia que parece gambiarra até virar produto, paper e empresa gigante.

Outra técnica é a paravirtualização. Nela, o sistema operacional convidado sabe que está virtualizado. Em vez de fingir que está numa máquina real o tempo todo, ele coopera com o hipervisor usando chamadas explícitas, chamadas de hypercalls.

Isso reduz parte do custo de emulação, mas cobra outro preço: o sistema convidado precisa ser adaptado ou ter suporte específico. É mais honesto, mas menos mágico. E, como todo mundo sabe, software honesto demais às vezes perde para software que mente com uma interface melhor.

Com o tempo, Intel VT-x e AMD-V trouxeram suporte de virtualização por hardware para o x86. Isso permitiu que o processador tivesse modos e estruturas voltadas especificamente para executar convidados de forma mais eficiente e segura. Depois vieram recursos como EPT e NPT para ajudar na virtualização de memória.

A moral é simples: virtualização começou como uma técnica de software tentando contornar hardware inadequado. Depois o hardware aprendeu o truque e fingiu que sempre esteve tudo sob controle.

O custo da virtualização

Toda abstração cobra aluguel.

No caso da virtualização, o custo aparece sempre que o hipervisor precisa intervir. Quando uma VM roda instruções comuns diretamente na CPU, ótimo. Quando acontece uma operação sensível, uma interrupção, uma falha de página, uma operação de E/S ou uma troca de contexto mais complicada, o hipervisor pode precisar assumir.

Esse evento costuma ser chamado de VM exit. A CPU sai do modo de execução do convidado e volta para o hipervisor. O hipervisor entende o que aconteceu, decide o que fazer, emula ou autoriza a operação e devolve o controle para a VM.

O pedágio de uma operação sensível

O objetivo de uma virtualização eficiente é reduzir esses pedágios.

CPU costuma ser a parte menos problemática quando há suporte de hardware. Memória é mais chata. E/S costuma ser a festa do sofrimento.

A regra prática é: quanto mais o convidado puder executar sem interrupção, melhor. O hipervisor ideal é aquele que quase nunca aparece. É tipo infraestrutura boa: ninguém elogia, mas todo mundo percebe quando fica no caminho.

Memória virtual em cima de memória virtual

A parte de memória é onde o Tanenbaum começa a parecer um louco genial.

Em um sistema operacional normal, processos usam endereços virtuais. O kernel mantém tabelas de páginas que traduzem esses endereços virtuais para endereços físicos na RAM. A MMU faz parte dessa tradução em hardware.

Com virtualização, o sistema convidado também acha que controla a memória física. Só que a “memória física” da VM não é a memória física real. É memória física convidada, que precisa ser mapeada para memória física da máquina hospedeira.

Então temos uma pilha de traduções:

Tradução de memória em uma VM

Sem suporte especial, uma técnica comum era usar shadow page tables. O hipervisor mantinha tabelas de páginas sombra que refletiam as intenções do sistema convidado, mas apontavam para a memória real correta. Funcionava, mas era trabalhoso. Sempre que o convidado mudava suas tabelas, o hipervisor precisava acompanhar.

Depois vieram as tabelas de páginas aninhadas, como EPT no mundo Intel e NPT no mundo AMD. A ideia é deixar o hardware ajudar nas duas traduções: do endereço virtual do processo para o endereço físico do convidado, e do endereço físico do convidado para o endereço físico real.

Isso reduz bastante o trabalho do hipervisor, mas não elimina todos os custos. TLB, invalidações, caches, páginas grandes, NUMA e overcommit continuam existindo para lembrar que abstração é linda até bater em hardware.

A frase mais importante dessa seção é: dentro de uma VM, até a memória física é uma abstração.

Bonito, né? Horrível também.

Virtualização de E/S: onde a fantasia apanha

Se CPU é relativamente elegante e memória é conceitualmente diabólica, E/S é o setor de achados e perdidos da virtualização.

Dispositivos são complicados. Placas de rede, discos, controladoras, GPUs, interrupções, DMA, filas, buffers, drivers e firmware entram na conversa. Um sistema operacional espera conversar com hardware real, mas a VM está conversando com uma representação virtual.

Existem três caminhos principais.

O primeiro é emular um dispositivo conhecido. O hipervisor apresenta uma placa de rede, uma controladora de disco ou outro dispositivo que o convidado já sabe usar. A vantagem é compatibilidade. A desvantagem é custo. Emular hardware antigo para enganar um sistema moderno é o equivalente computacional de contratar um ator para fingir ser impressora matricial.

O segundo caminho é usar drivers paravirtualizados. Em vez de fingir um hardware real, o convidado usa um driver feito para conversar com o ambiente virtualizado. VirtIO é o exemplo clássico no ecossistema KVM/QEMU. Isso reduz overhead porque o convidado e o hipervisor param de fingir tanto.

O terceiro caminho é passthrough. Nesse modelo, um dispositivo real, ou parte dele, é entregue diretamente a uma VM. Tecnologias como IOMMU e SR-IOV ajudam nisso. SR-IOV, por exemplo, permite que um dispositivo PCIe exponha funções virtuais para diferentes VMs.

O desempenho melhora, mas a flexibilidade piora. Migração fica mais difícil. Isolamento exige mais cuidado. A VM fica mais presa ao hardware real.

É sempre o mesmo acordo: quanto mais perto do hardware, melhor o desempenho e pior a magia. Quanto mais abstrato, mais portátil e mais caro.

Hipervisores são micronúcleos feitos certo?

O capítulo levanta uma provocação ótima: hipervisores seriam micronúcleos feitos do jeito certo?

A comparação faz sentido. Um micronúcleo tenta manter no núcleo apenas o mínimo necessário, movendo serviços para fora da camada privilegiada. Um hipervisor também tenta ser uma camada pequena e poderosa, responsável por isolamento, comunicação e controle de recursos.

Na paravirtualização, essa semelhança fica ainda mais evidente. O sistema convidado faz hypercalls para pedir serviços ao hipervisor, um pouco como processos poderiam pedir serviços a servidores em uma arquitetura de micronúcleo.

Mas a comparação tem limite.

Micronúcleo é uma proposta de arquitetura de sistema operacional. Hipervisor é uma camada para executar e isolar sistemas operacionais, muitas vezes sem que eles tenham sido projetados para isso. O hipervisor não venceu porque era filosoficamente mais puro. Ele venceu porque rodava sistemas existentes.

E essa é uma lição brutal de engenharia: compatibilidade frequentemente derrota elegância.

Você pode ter a arquitetura mais bonita do mundo. Se ela exige reescrever tudo, boa sorte convencendo o mercado. VMware não precisou convencer todo mundo a reescrever seus sistemas. Ele deixou os sistemas acharem que ainda estavam sozinhos na máquina.

Isso é quase poesia. Poesia com instrução privilegiada e emulação de E/S, mas poesia.

Aplicações virtuais e a pré-história do “funciona na minha máquina”

O livro também fala de aplicações virtuais: imagens completas contendo sistema operacional, bibliotecas, dependências e aplicação. A ideia é empacotar tudo que uma aplicação precisa para rodar em um ambiente previsível.

Isso resolve uma dor antiga: dependências.

Antes de containers dominarem o vocabulário, VMs já eram usadas para distribuir ambientes completos. Em vez de pedir para o usuário instalar biblioteca, runtime, driver, versão específica de compilador e rezar para não quebrar o sistema, você entrega uma imagem.

É pesado? Sim. Mas funciona.

Containers mudaram parte dessa conversa porque oferecem um modelo mais leve de empacotamento e isolamento. Só que é importante não confundir as coisas: container não é VM leve. Container compartilha o kernel do host. VM executa seu próprio kernel convidado.

VM e container não são a mesma gambiarra

É por isso que microVMs como Firecracker são interessantes: elas tentam ocupar o meio do caminho. Mais isolamento que container tradicional, menos peso que VM convencional. Em outras palavras, porque aparentemente a indústria olhou para duas abstrações e pensou: “faltou uma terceira para confundir melhor”.

VMs em CPUs multinúcleo

Virtualização fica mais interessante quando entram múltiplos núcleos.

Uma VM pode ter várias vCPUs. O hipervisor precisa mapear essas vCPUs para CPUs físicas. Isso parece simples até lembrar que sistemas operacionais convidados também têm seus próprios escalonadores. Então existe um escalonador dentro da VM e outro fora dela.

O convidado acha que está escalonando threads em CPUs disponíveis. O hipervisor está escalonando vCPUs em CPUs reais. Se o host está carregado, uma vCPU pode ser pausada enquanto outra continua. Para o convidado, isso pode parecer uma máquina estranha, meio bêbada, onde CPUs somem temporalmente sem pedir desculpas.

Isso afeta locks, temporização, desempenho e previsibilidade. Sistemas que assumem controle fino do tempo podem sofrer bastante quando estão virtualizados.

Na prática, isso explica por que dimensionar VM não é apenas “colocar mais vCPU”. Às vezes mais vCPU piora o desempenho, especialmente se aumenta contenção ou dificulta escalonamento no host. Infraestrutura adora punir soluções intuitivas.

Licenciamento: a parte burocrática também morde

Tanenbaum dedica uma seção a licenciamento porque virtualização não cria apenas problemas técnicos. Ela também cria perguntas jurídicas e comerciais.

Se você roda dez VMs com o mesmo sistema operacional, quantas licenças precisa? Se migra uma VM entre hosts, a licença acompanha? Se faz snapshot, clone ou checkpoint, isso conta como nova instância? Se o software licencia por CPU, ele fala de CPU física, socket, core, vCPU ou algum conceito inventado pelo departamento comercial depois de três cafés e um trimestre ruim?

Virtualização torna cópia e movimentação de ambientes muito mais fáceis. Modelos de licenciamento antigos nem sempre acompanham isso bem.

Essa parte é menos glamourosa, mas essencial. Em datacenter, muitas decisões de arquitetura são afetadas não apenas por desempenho ou segurança, mas por licença. Sim, às vezes a tabela comercial manda mais que o kernel. É triste, mas pelo menos não é surpreendente.

Nuvem: virtualização com API, datacenter e boleto

A nuvem entra no capítulo como consequência natural da virtualização.

Se é possível dividir uma máquina física em várias máquinas virtuais isoladas, também é possível vender essas máquinas sob demanda. O provedor opera o datacenter, compra hardware, gerencia energia, rede, refrigeração, falhas e capacidade. O usuário recebe uma interface para criar recursos quando precisa.

A definição clássica do NIST descreve cloud computing como acesso sob demanda, via rede, a um conjunto compartilhado de recursos configuráveis que podem ser provisionados e liberados rapidamente. Essa definição é menos sexy que um keynote, mas bem mais útil.

Modelos comuns:

  • IaaS, infraestrutura como serviço, quando você aluga VMs, redes, discos e recursos básicos;
  • PaaS, plataforma como serviço, quando a infraestrutura fica mais escondida e você entrega aplicação;
  • SaaS, software como serviço, quando você usa o produto pronto.

Na prática, cloud é virtualização mais automação, rede virtual, armazenamento distribuído, IAM, observabilidade, billing, APIs, regiões, zonas, SLAs e uma quantidade saudável de siglas que parecem criadas para manter consultorias vivas.

A VM não é a nuvem inteira. Mas sem a ideia de virtualização, a nuvem moderna seria muito menos flexível.

Migração de VM: mover a mentira sem acordar o convidado

Uma das partes mais elegantes do capítulo é a migração de máquina virtual.

A ideia é mover uma VM de uma máquina física para outra mantendo a execução. Isso permite manutenção, balanceamento de carga e tolerância a falhas com menos interrupção. Parece mágica, mas é engenharia com cópia de memória, estado de CPU, dispositivos e armazenamento.

O desafio é que a VM continua modificando memória enquanto você tenta copiá-la. Uma estratégia comum é copiar páginas de memória enquanto a VM ainda roda, rastrear páginas alteradas, copiar de novo as páginas sujas e, no final, pausar brevemente a VM para transferir o estado restante.

Migração viva de uma VM

A migração viva é um bom exemplo da beleza meio absurda da virtualização: se a máquina já é uma abstração, talvez dê para mover a abstração enquanto ela ainda está sonhando que é hardware.

Claro, isso exige compatibilidade de CPU, rede, armazenamento e dispositivos. Nada é gratuito. Mas a ideia é poderosa.

Checkpointing: salvar o videogame da infraestrutura

Checkpointing é o ato de salvar o estado de uma VM para retomar depois.

Pense em “save state” de emulador, só que para sistemas operacionais inteiros e com implicações muito mais chatas. Um checkpoint pode incluir memória, estado de CPU, dispositivos virtuais e disco, dependendo da implementação.

Isso é útil para tolerância a falhas, testes, depuração, rollback e manutenção. Também cria problemas interessantes: estado externo pode mudar. Conexões de rede podem expirar. Sistemas distribuídos podem não gostar de descobrir que uma máquina voltou no tempo.

Snapshot parece simples no painel. Conceitualmente, é uma pequena viagem temporal. E viagem temporal em sistemas distribuídos costuma terminar em bug com cara de possessão demoníaca.

VMware: quando o x86 virou um problema de negócios

O estudo de caso de VMware é um dos melhores trechos porque mostra a diferença entre teoria e produto.

Virtualização já existia em mainframes. O problema era fazer isso funcionar bem no x86, uma arquitetura popular, barata e cheia de software existente, mas que não era elegantemente virtualizável no modelo clássico.

A VMware atacou esse problema com uma combinação de técnicas. O VMware Workstation rodava como aplicação sobre um sistema operacional hospedeiro, aproveitando drivers e suporte do host. Ao mesmo tempo, usava um monitor de máquina virtual para executar o convidado com controle e eficiência.

A parte genial foi lidar com as instruções problemáticas do x86 usando tradução binária e outras técnicas. Em vez de esperar a arquitetura ficar perfeita, a VMware construiu uma camada capaz de contornar suas falhas práticas.

Depois, com ESX Server, a VMware levou a ideia para o datacenter. Em vez de rodar uma aplicação por servidor físico, empresas podiam consolidar múltiplas VMs no mesmo hardware. Isso reduzia subutilização, simplificava provisionamento e permitia recursos como migração viva.

Essa mudança parece óbvia hoje, mas era enorme. Antes, comprar servidor físico para cada aplicação era comum. Com virtualização, servidor virou pool de recursos.

É uma das grandes viradas da infraestrutura moderna: o hardware deixou de ser o lugar fixo onde o sistema mora e virou capacidade alocável.

O que o capítulo ainda ensina muito bem

Mesmo sendo uma edição de 2015/2016, a base conceitual do capítulo continua excelente.

KVM, QEMU, Firecracker, Nitro, containers, Kubernetes, confidential computing e afins mudaram o cenário, mas não invalidaram os conceitos. Pelo contrário: eles ficam mais fáceis de entender quando você já compreende hipervisor, trap, emulação, memória virtual, E/S e isolamento.

O que mudou foi a pilha ao redor.

Hoje, virtualização está em clouds públicas, clusters privados, desktops de desenvolvimento, CI, sandboxes, plataformas serverless, ambientes de segurança e até em runtimes que tentam parecer containers. A ideia saiu do mainframe, passou pelo desktop, dominou o datacenter e voltou disfarçada em microVM.

O vocabulário mudou. A mentira continuou.

O ponto central

Virtualização não é apenas rodar um sistema dentro de outro. É uma técnica para transformar recursos físicos em interfaces controladas, isoladas e multiplexadas.

Ela depende de uma negociação constante entre ilusão e controle. O convidado precisa acreditar que manda. O hipervisor precisa garantir que ele não manda demais. O hardware precisa ajudar. A memória precisa ser traduzida duas vezes sem virar uma tragédia. A E/S precisa funcionar sem transformar cada pacote de rede em uma novela. O licenciamento precisa não arruinar a arquitetura. A nuvem precisa vender tudo isso como se fosse simples.

E, de alguma forma, tudo isso funciona bem o suficiente para sustentar boa parte da computação moderna.

Essa é a genialidade do capítulo do Tanenbaum: ele pega um termo que muita gente usa de forma rasa e mostra a pilha inteira de problemas escondida por baixo. CPU, memória, E/S, hipervisor, nuvem, VMware, migração, checkpointing. Tudo conectado.

No fim, virtualização é uma mentira bem implementada. E como toda boa mentira em computação, quando funciona direito, vira abstração.

Referências e leituras recomendadas