Criando um ambiente de desenvolvimento com o QEMU

Pdf iconBaixar versão em PDF


Resumo

Emuladores são ferramentas essenciais desde o desenvolvimento de aplicativos que serão executados em uma plataforma diferente da qual são desenvolvidos, até a criação de aplicações que trabalham diretamente com o hardware, como sistemas operacionais. Este artigo aborda a criação de um ambiente completo de desenvolvimento utilizando o emulador QEMU[1], que é licensiado sob a GPL[2].
    

  1. Introdução
  2.      Em computação, um Emulador é um software capaz de executar instruções de uma outra, ou da mesma arquitetura em que é executado, podendo criar ambientes virtuais completos. Os exemplos mais comuns são emuladores de vídeo games, que executam jogos (software) de um determinado hardware em PCs comuns, sendo possível, por exemplo, executar jogos do Atari em um PC.
        

    O QEMU[1] é um emulador de máquina e um virtualizador. Como emulador, é capaz de executar instruções de processadores de várias arquiteturas, dentre elas x86[3], ARM[4], SPARC[5], PowerPC[6], e várias outras. Assim, num computador x86 pode-se ter várias instâncias do QEMU emulando máquinas x86, ARM, podendo executar outros sistemas operacionais ou até mesmo diferente tipos de BIOS. Já como virtualizador, o QEMU é capaz somente de executar instruções x86 rodando em uma máquina x86, isto porque o QEMU cria um ambiente virtual mas executa determinadas instruções diretamente no processador em que está sendo executado.
        

    Toda a documentação (lista de processadores suportados, etc) pode ser obtida no site oficial do projeto[1]. Neste artigo será tratado apenas o modo emulador, portanto, a partir de agora, qualquer referência ao QEMU refere-se ao modo emulador.
        

    O QEMU pode ser extramamente util não só no desenvolvimento de sistemas operacionais, mas também de distribuições Linux, as figuras abaixo mostram a sua execução tanto no Windows®   quando no Linux:



    QEMU executando no Windows®




    QEMU executando no Linux

  3. Instalação
  4. O QEMU foi escrito para Linux, mas possui ports para Windows®   e MAC, que podem ser obtidos em:

    A maioria das distribuições linux já possuem pacotes do QEMU. No Ubuntu e família, a instalação pode ser feita através do comando:

    $ sudo apt-get install qemu
    

    • Compilando o QEMU
    • O código-fonte do QEMU pode ser obtido no site oficial[1] do projeto. A compilação deve ser feita com a versão 3.x do gcc, pois versões superiores podem gerar problemas na compilação ou produzirem binários instáveis. Atualmente a maioria as distriuições já vêm com a versão 4.x do gcc, sendo necessária a instalação da versão 3, para verificar qual a versão instalada no sistema utilize o comando gcc -v:

      $ gcc -v
      Usando especificações internas.    
      Destino: i486-linux-gnu
      Configurado com: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.2 --program-suffix=-4.2 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-targets=all --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
      Modelo de fluxo de execução (thread): posix
      gcc versão 4.2.3 (Ubuntu 4.2.3-2ubuntu7)
      $
      

      No exemplo acima a versão instalada (padrão) do sistema é a 4.2.3, necessitando assim da instalação da versão 3.x. No Ubuntu e família pode-se instalar a versão 3.4:

      $ sudo apt-get install gcc-3.4
      

      Possuindo as ferramentas básicas de compilação (gcc, make) a compilação e instalação é simples:

      $ tar -xvzf qemu-0.9.1.tar.gz
      $ cd qemu-0.9.1
      $ ./configure
      $ make
      $ sudo make install
      

      Caso a versão padrão do gcc não seja 3.x, deve-se informar qual versão a ser utilizada, por exemplo:

      $ ./configure --cc=gcc-3.4
      

    • Módulo de aceleração
    • O qemu pode ser acelerado através da instalação de um módulo para o kernel que o permite executar instruções diretamente no processador. Este módulo, o kqemu, já possui pacote para a maioria das distribuições, mas também pode ser baixado no site oficial do projeto. A compilação e instalação é simples:

      $ tar -xvzf kqemu-1.3.0pre11.tar.gz
      $ cd kqemu-1.3.0pre11/
      $ ./configure
      $ make
      $ sudo make install
      

      No Ubuntu e família o kqemu pode ser compilado e instalado através dos comandos:

      $ sudo apt-get install kqemu-source module-assistant
      $ sudo module-assistant
      

      Na janela do module-assitant deve-se selecionar as opções PREPARE e depois SELECT, marcando o módulo kqemu para compilação. Em seguida, basta selecionar a opções BUILD e INSTALL.
          

      • Configurando kqemu
      • Para ser utilizado o módulo deve ser carregado no kernel, criando o dispositivo /dev/kqemu. Por padrão, somente o root terá acesso a este dispositivo, assim o qemu deverá ser executado com o root para ter a aceleração ativada. Para permitir que outros usuários possam executar o qemu com aceleração siga os passos abaixo:

        1. Crie o grupo qemu:
        2. $ sudo groupadd qemu
          

        3. Crie o arquivo /etc/udev/rules.d/40-kqemu.rules com o seguinte conteudo:
        4. 1
          2
          
          # Arquivo para setar permissoes do dispositivo de aceleracao do kqemu
          KERNEL=="kqemu" GROUP="qemu"
        5. Adicione kqemu no arquivo /etc/modules para carregar o módulo na inicialização do sistema:
        6. $ su
          root# echo kqemu >> /etc/modules
          

          O módulo também pode ser carregado manualmente, através do comando:

          $ sudo modprobe kqemu
          

        7. Adicione o usuário que poderá utilizar o módulo de aceleração no grupo qemu:
        8. $ sudo usermod -a -G qemu usuario
          

        Somente os usuários pertencentes ao grupo qemu possuirão acesso a aceleração, que é ativada quando o qemu é chamado com o parâmetro -kernel-kqemu, por exemplo:

        $ qemu -kernel-kqemu -hda minix204.img -boot c
        

  5. Executando o QEMU
  6. O qemu é composto por vários executáveis, um para cada tipo de arquitetura:

    $ qemu
    qemu                   qemu-m68k              qemu-sh4               qemu-system-m68k       qemu-system-ppcemb
    qemu-alpha             qemu-make-debian-root  qemu-sh4eb             qemu-system-mips       qemu-system-sh4
    qemu-arm               qemu-mips              qemu-sparc             qemu-system-mips64     qemu-system-sh4eb
    qemu-armeb             qemu-mipsel            qemu-sparc32plus       qemu-system-mips64el   qemu-system-sparc
    qemu-cris              qemu-ppc               qemu-sparc64           qemu-system-mipsel     qemu-system-x86_64
    qemu-i386              qemu-ppc64             qemu-system-arm        qemu-system-ppc        qemu-x86_64
    qemu-ppc64abi32        qemu-system-cris       qemu-system-ppc6
    $ qemu
    

    Iremos utilizar o executável qemu, que é um emulador de PC (arquitetura x86).

    O qemu utiliza a BIOS do projeto Bochs e suporta multiprocessamento simétrico (SMP) podendo emular até 255 CPUs. Os seguintes periféricos são simulados:

    • Ponte PCI i440FX e Ponte PCI-ISA PIIX3
    • Placa de vídeo VGA PCI Cirrus CLGD 5446
    • Placa de vídeo genérica com extensões VESA do Bochs
    • Mouse e teclado PS/2
    • 2 interfaces IDE com suporte a disco rígido e CD-ROM
    • Disquetes
    • Adaptadores de Rede PCI/ISA
    • Portas seriais
    • Placa de som Creative SoundBlaster 16
    • Placa de som ENSONIQ AudioPCI ES1370
    • Adlib(OPL2) - Chip compatível com o Yamaha YM3812
    • Controlador USB PCI UHCI e hub USB virtual

    O qemu deve ser executado da seguinte forma:

    • qemu [opções] [arquivo_imagem]

    arquivo_imagem é o arquivo de imagem que o qemu irá emular. Este arquivo deve ser a imagem de um disco. O site http://www.oszoo.org contém centenas de imagem prontas (com diversos sistemas operacionais instalados) para serem utilizadas no qemu, por exemplo, acesse a sessão download e baixe a imagem do Minix 3.1.1 (Minix3_1_1 x86.tar). Para inciar a emulação basta descompactar a imagem e executar o qemu:

    
    $ tar -xvf minix3_1_1_x86.tar
    $ cd minix3_1_1_x86/
    $ qemu -hda minix_3_1_1.img -boot c
    

    A figura abaixo ilustra a emulação do Minix:



    QEMU emulando o Minix 3.1.1

    Alguns atalhos facilitam o uso do qemu:

    • Ctrl+Alt - Ativa/Desativa o mouse dentro do qemu
    • Ctrl+Alt+F - Ativa/Desativa tela cheia (fullscreen)
    • Ctrl+Alt+2 - Entra no monitor do qemu
    • Ctrl+Alt+1 - Entra na tela de emulação (sai do monitor)

    O monitor permite que várias ações de máquina sejam executadas: reset, adicionar/remover dispositivos USB, enviar eventos de teclado e mouse, capturar tela e áudio, dentre outras. Para listar os comandos disponíveis digite help.

    O qemu também pode ser util em algumas tarefas, como:

    • Testar um disquete de boot:
    • $ sudo qemu -fda /dev/fd0 -boot a
      

    • Testar um live CD:
    • $ qemu -cdrom /dev/cdrom -boot d
      

    • Testar a iso de um live CD:
    • $ qemu -cdrom imagem.iso -boot d
      

    O qemu possui uma infinidade de opções, todas descritas na documentação oficial do projeto http://bellard.org/qemu/qemu-doc.html, as mais básicas são:

    • -M machine : Especifíca qual máquina emular, para listar as disponíveis utilize -M ?
    • -fda ou -fdb file : file indica o arquivo de imagem para ser emulado como um disquete (drive a ou b). O arquivo de dispositivo correspondente ao drive de disquete (por exemplo, /dev/fd0) também pode ser utilizado.
    • -hda, -hdb, -hdc ou -hdd file : file indica o arquivo de imagem para ser emulado como um disco rígido (0, 1, 2 ou 3).
    • -cdrom file : file indica o arquivo para ser emulado como um CD-ROM. O arquivo de dispositivo correspondente ao drive de CD também pode ser utilizado (por exemplo, /dev/cdrom).
    • -boot X : X indica qual dispositivo “bootar” (a - disquete, c - HD, d - cdrom, n - Rede).

  7. Criando imagens
  8. O utilitário qemu-img, que acompanha o qemu, permite a criação de imagens de disco em diversos formatos, inclusive no formato vmdk (utilizado pelo VMware 3 e 4). Sua sintaxe é simples, por exemplo, para criar a imagem teste.img de 30MB no formato qcow (com criptografia), utilize o comando abaixo:

    $ qemu-img create -f qcow -e teste.img 30M
    

    O qemu também suporta imagens raw (sem formato, contém apenas o conteúdo do disco), que serão abordadas neste artigo. A criação deste tipo de imagem é simples e pode ser feita através do comando dd:

    $ dd if=/dev/zero of=teste.img bs=1024 count=10K
    

    Neste caso, a imagem teste.img, de 10MB é criada. Para saber mais sobre o comando dd e seus multiplicadores (b, K, MB, etc), consulte man dd.

    Criar o arquivo de imagem é apenas o primeiro passo na montagem de um sistema completo para emulação. Para que um sistema com boot possa ser instalado na imagem, é preciso que a mesma seja emulada como um dispositivo de bloco pelo Linux (haja emulação!!), este processo é feito através de dois utilitários: losetup, que faz parte do pacote util-linux-ng, presente por padrão em várias distribuições, e kpartx, que também possui pacote para inúmeras distros.
        

    DICA:
    Para que o kpartx funcione é preciso que a opção Device Drivers->Multiple devices driver support->Device mapper support esteja ativada na configuração do kernel ou se estiver como módulo, o mesmo deve ser carregado (modprobe dm_mod).
        

    O utilitário debootstrap permite a instalação de um sistema debian dentro de um diretório qualquer, o que é extremamente útil na criação de imagens. A idéia é simples: Criar uma imagem através do dd, emular um dispositivo de blocos com essa imagem (para criar as partições e instalar o sistema através do debootstrap), deixando-a pronta para ser emulada posteriormente pelo qemu.
        

    No Ubuntu e família, os utilitários citados podem ser instalados através do comando:

    $ sudo apt-get install debootstrap kpartx
    

    Primeiro passo, criar o arquivo de imagem, iremos utilizar um arquivo com 671MB:

    $ dd if=/dev/zero of=mydebian.img bs=4096 count=160K
    

    Segundo passo, verificar qual dispositivo de loop pode ser utilizado para emularmos a imagem como um dispositivo de bloco:

    $ sudo losetup -f
    /dev/loop0
    

    Terceiro passo, montar a imagem como um dispositivo de bloco e ajustar o número de cilindros do HD “virtual”:

    
    $ sudo losetup /dev/loop0 mydebian.img
    $ sudo kpartx -av /dev/loop0
    gpt: 0 slices
    dos: 4 slices
    $
    $ sudo fdisk /dev/loop0
    
    Comando (m para ajuda): x
    
    Comando avançado (m para ajuda): c
    Número de cilindros (1-1048576, padrão 81): 81
    
    Comando avançado (m para ajuda): w
    A tabela de partições foi alterada!
    
    Chamando ioctl() para reler tabela de partições.    
    
    AVISO: Re-leitura da tabela de partição falhou com erro 22: Argumento inválido.    
    O kernel ainda está usando a tabela antiga.    
    
    A nova tabela será usada no próximo reboot.    
    Sincronizando discos.    
    

    Quarto passo, criar as partições:

    $ sudo fdisk /dev/loop0
    
    Comando (m para ajuda): n
    Comando - ação
       e   estendida
       p   partição primária (1-4)
    p
    Número da partição (1-4): 1
    Primeiro cilindro (1-81, padrão 1):
    Usando valor padrão 1
    
    Último cilindro ou +tamanho ou +tamanho M ou  +tamanho K (1-81, padrão 81): 75
    
    Comando (m para ajuda): n
    Comando - ação
       e   estendida
       p   partição primária (1-4)
    p
    Número da partição (1-4): 2
    Primeiro cilindro (76-81, padrão 76):
    Usando valor padrão 76
    Último cilindro ou +tamanho ou +tamanho M ou  +tamanho K (76-81, padrão 81):
    Usando valor padrão 81
    
    Comando (m para ajuda): t
    Número da partição (1-4): 2
    Código hexadecimal (digite L para listar os códigos): 82
    O tipo da partição 2 foi alterado para 82 (Linux swap / Solaris)
    
    Comando (m para ajuda): a
    Número da partição (1-4): 1
    
    Comando (m para ajuda): p
    
    Disco /dev/loop0: 671 MB, 671088640 bytes
    255 heads, 63 sectors/track, 81 cylinders
    Units = cilindros of 16065 * 512 = 8225280 bytes
    Disk identifier: 0x849a0b60
    
     Dispositivo Boot Início Fim Blocos Id Sistema
    /dev/loop0p1   *           1          75      602406   83  Linux
    /dev/loop0p2              76          81       48195   82  Linux swap / Solaris
    
    Comando (m para ajuda): w
    A tabela de partições foi alterada!
    
    Chamando ioctl() para reler tabela de partições.    
    
    AVISO: Re-leitura da tabela de partição falhou com erro 22: Argumento inválido.    
    O kernel ainda está usando a tabela antiga.    
    A nova tabela será usada no próximo reboot.    
    Sincronizando discos.    
    

    Repare na configuração das partições, a primária conterá o sistema (Linux) e a secundária será utilizada para swap (Linux swap / Solaris).

    Quinto passo, “emular” as partições:

    $ sudo kpartx -dv /dev/loop0
    gpt: 0 slices
    dos: 4 slices
    $ sudo kpartx -av /dev/loop0
    gpt: 0 slices
    dos: 4 slices
    add map loop0p1 (254:0): 0 1204812 linear /dev/loop0 63
    add map loop0p2 (254:1): 0 96390 linear /dev/loop0 1204875
    

    Pronto, a imagem está funcionando como um dispositivo de blocos comum, o quinto passo consiste em gravar o sistema de arquivo e montar a partição de sistema:

    $ sudo mkfs.ext3 /dev/mapper/loop0p1
    $ sudo mount /dev/mapper/loop0p1 /mnt/
    

    Sexto passo, instalar o sistema debian na imagem, o sistema escolhido foi o debian (versão etch) para i386:

    sudo debootstrap --arch=i386 etch /mnt http://limbo.ime.usp.br/debian
    

    O processo pode leval algum tempo, após concluído, a imagem mydebian.img já conterá o sistema debian instalado, porém sem nenhum kernel. O sétimo passo consiste em instalar um kernel na imagem:

    
    $ sudo mount proc /mnt/proc -t proc
    $ sudo mount -o bind /dev /mnt/dev/
    $ sudo chroot /mnt/ /bin/bash
    

    Após o comando chroot estaremos dentro do “ambiente” da imagem, bastando apenas instalar o novo kernel:

    $latex  export PS1="chroot \w $ "
    chroot / $ apt-get install grub
    chroot / $ echo "do_initrd=yes" > /etc/kernel-img.conf
    chroot / $ apt-get install linux-image-2.6-486
    

    Responda sim para todas as perguntas.

    Continuando, o oitavo passo é instalar o grub para deixar a imagem “bootável”. Aqui precisaremos de um pequeno truque, será necessário a criação de um link na pasta /dev/mapper para representar o dispositivo /dev/loop0 (no nosso caso) de acordo com as partições criadas para que a instalação do grub proceda de forma correta, os comandos abaixo farão a criação do link necessário (de acordo com seu sistema) automaticamente:

    chroot / $ apt-get install grub
    chroot / $ mkdir /boot/grub
    chroot / $ cp -r /usr/lib/grub/i386-pc/* /boot/grub/
    chroot / $ ls /dev/mapper/loop*p1 | sed "s#/dev/mapper/loop\(.*\)p[0-9]# -s /dev/loop\1 /dev/mapper/loop\1p#" | xargs ln
    

    Depois de criado o link (no nosso caso, /dev/mapper/loop0p), a instalação do grub pode ser feita:

    chroot / $ grub --no-floppy
    
        GNU GRUB  version 0.97  (640K lower / 3072K upper memory)
    
           [ Minimal BASH-like line editing is supported.   For
             the   first   word,  TAB  lists  possible  command
             completions.  Anywhere else TAB lists the possible
             completions of a device/filename. ]
    
    grub> device (hd1) /dev/mapper/loop0p
    
    grub> root (hd1,0)
     Filesystem type is ext2fs, partition type 0x83
    
    grub> setup (hd1)
     Checking if "/boot/grub/stage1" exists... yes
     Checking if "/boot/grub/stage2" exists... yes
     Checking if "/boot/grub/e2fs_stage1_5" exists... yes
     Running "embed /boot/grub/e2fs_stage1_5 (hd1)"...  15 sectors are embedded.    
    succeeded
     Running "install /boot/grub/stage1 (hd1) (hd1)1+15 p (hd1,0)/boot/grub/stage2 /boot/grub/menu.lst"... succeeded
    Done.    
    
    grub> quit
    chroot / $ update-grub
    Searching for GRUB installation directory ... found: /boot/grub
    Cannot determine root device.  Assuming /dev/hda1
    This error is probably caused by an invalid /etc/fstab
    Searching for default file ... Generating /boot/grub/default file and setting the default boot entry to 0
    Searching for GRUB installation directory ... found: /boot/grub
    Testing for an existing GRUB menu.lst file ...    
    
    Could not find /boot/grub/menu.lst file. Would you like /boot/grub/menu.lst generated for you? (y/N) Y
    Searching for splash image ... none found, skipping ...    
    Found kernel: /boot/vmlinuz-2.6.18-6-486
    Updating /boot/grub/menu.lst ... done
    
    chroot / $ echo "(hd0) /dev/hda" > /boot/grub/device.map
    chroot / $
    

    Nono e penultimo passo, a imagem está quase pronta, basta apenas algumas configurações no sistema, gravação da senha de root e uma limpeza para manter a “sanidade”:

    chroot / $ cat > /etc/fstab
    # My fstab
    proc /proc proc defaults 0 0
    /dev/hda1 / ext3 defaults,errors=remount-ro 0 1
    /dev/hda2 none swap sw 0 0
    
    (Pressione Ctrl+D para sair do cat)
    
    chroot / $
    chroot / $ echo "mydebian" > /etc/hostname
    chroot / $ cat > /etc/hosts
    127.0.0.1 localhost
    127.0.0.1 mydebian
    
    (Pressione Ctrl+D para sair do cat)
    
    chroot / $
    chroot / $ apt-get clean
    chroot / $ passwd
    Enter new UNIX password:
    Retype new UNIX password:
    passwd: password updated successfully
    chroot / $ exit
    

    Ultimo passo (ufa!), basta “desmontarmos” as partições e dispositivos, a imagem está pronta para ser emulada no qemu!

    $ sudo umount /mnt/proc/
    $ sudo umount /mnt/dev/
    $ sudo umount /mnt/
    $ sudo rm /dev/mapper/loop0p
    $ sudo kpartx -dv /dev/loop0
    $ sudo losetup -d /dev/loop0
    

    Imagem pronta, vamos testar!

    $ qemu -hda mydebian.img -boot c
    



    Imagem sendo emulada no QEMU

  9. Desenvolvendo
  10. Após os 10 passos citados no tópico anterior a imagem já está utilizável, sendo emulada perfeitamente no qemu, porém, o sistema instalado é básico, sem servidor X, gcc, etc.
        

    A internet pode ser ativada por DHCP:

    $ dhclient eth0
    

    Alguns pacotes podem ser instalados para tornar a imagem um ambiente de desenvolvimento mais amigável:

    $ apt-get intall xserver-xorg xfce4 xfonts-100dpi xfonts-75dpi xfonts-base xfonts-encodings xfonts-scalable xfonts-utils gdm pciutils build-essential
    

    Outras personalizações ficam a critério do leitor.
        

  11. Conclusão
  12. Este artigo apresentou o emulador QEMU, uma ferramenta robusta e extremamente útil, que pode ajudar no desenvolvimento de distribuições Linux, Sistemas Operacionais e outras aplicações. Um método para criação de uma imagem de disco foi apresentado, assim como instalar um sistema debian nesta imagem.
        

  13. Bibliografia
  14. Creative Commons License
    “Criando um ambiente de desenvolvimento com o QEMU”, por Renê de Souza Pinto, é licensiado sob a Creative Commons Atribuição-Uso Não-Comercial 2.5 Brasil License.

Deixe um comentário