Configurando User Mode Linux [UML]
e um ambiente para desenvolvimento
e depuração do kernel do Linux


Copyright (c) 2007 Rodrigo Ferreira Baroni .
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

Uma Máquina Virtual é um termo que possui diferentes significados na Ciência da Computação. Esta página refere-se a como configurar e construir uma máquina virtual com significado de um computador fictício criado por software, sendo executado em forma de aplicação no espaço do usuário, dentro de um sistema GNU/Linux. Tal software que simula o computador fictício descrito neste documento emprega a virtualização dos recursos no nível do sistema operacional; é composto por dois componentes:

1) Um sistema operacional executando no espaço do usuário - um kernel apropriado (e User-Mode-Linux é o projeto responsável por tal kernel); e
2) Programas no espaço do usuário - disponíveis sob um sistema de arquivos.

Este documento descreve os passos para criar tais componentes e integrá-los, obtendo no final uma máquina virtual GNU/Linux dentro de outra sendo executada no computador. Nó utilizamos na construção do sistema de arquivos, um sistema Debian, utilizando a ferramenta específica 'debbootstrap', mas tal ferramenta pode também ser instalada em outros sistemas conforme descrito no final deste documento, o que abrange este documento para ser utilizado por qualquer distribuição GNU/Linux. Após a criação da Máquina Virtual, é descrito como utilizá-la para o desenvolvimento e depuração do kernel do Linux, e o ambiente de desenvolvimento com os procedimentos para integração das alterações no kernel com a etapa de testes e depuração.

O documento é organizado em 4 partes:


A última atualização deste documento é de Abril 14, 2007 - fique atento, pois configurações mudam com o tempo, e as documentações se tornam obsoletas (e já há muita documentação na internet sobre configuração de uma máquina virtual utilizando user-mode-linux que está obsoleta). Contribuições são sempre bem vindas, caso encontre falhas/sugestões e ainda tempo e disposição, mande-a via baroni@ime.usp.br.

Agradecimentos à todos que contribuíram, ao professor Roberto Hirata do IME-USP que durante o curso de Sistemas Operacionais no instituto, permitiu e incentivou a elaboração deste documento enquanto fui monitor da disciplina, ao IME-USP e à CAPES pelas oportunidades que permitiram o envolvimento com este estudo, além dos desenvolvedores envolvidos no desenvolvimento de toda a plataforma GNU/Linux, base de computação séria e com compromisso com a preservação do livre acesso ao conhecimento da ciência e da tecnologia, incluindo os envolvidos no projeto User-Mode-Linux!


Parte I - Criando e configurando um kernel UML

O primeiro passo é criar um kernel UML. Um kernel UML é um kernel em que toda comunicação que seria feita com os dispositivos físicos afim de controlá-los e gerenciá-los é modificada, de modo que os recursos físicos sejam obtidos através do kernel tradicional sendo executado no computador. No início era um projeto disponível em forma de patch a ser aplicado no kernel, atualmente (desde a versão 2.6.9) está disponível no próprio kernel - não e´ mais necessário aplicar patches, com exceção dos patches de otimizações extras do blaisorblade. Segue abaixo os procedimentos necessários para gerar um kernel UML.


  • 1) Baixando a árvore do kernel do Linus com o git (apt-get install git-core)
  • Entre no diretório desejado a colocar a os fontes do kernel (tradicionalmente /usr/src):
    $ cd /usr/src/ 
    Obtenha a árvore:
    git-clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \
    				linux-2.6-linus-gittree 
    Isso vai criar o diretório linux-2.6-linus-git/ (em /usr/src/) . Esta árvore possui todo o histórico de desenvolvimento da árvore do kernel. É possível ver o que mudou em cada arquivo executando: $ gitk kernel/fork.c (por exemplo, para ver as alterações que têm sido feitas em tal arquivo, ou então só 'gitk' para ver o histórico de todos os arquivos). Para manter os fontes da árvore atualizado com o desenvolvimento, execute o comando: $git-pull (dentro do diretório) regularmente.


    2) Extraia uma versão estável da árvore Dentro do diretório .git/refs/tags/ há arquivos que indicam as marcações na árvore, respectivas ao estado da árvore quando nos lançamentos das versões: Entre no diretório:
    $ cd /usr/src/linux-2.6-linus-gittree
    Veja as marcações existentes:
    $ ls .git/refs/tags 
    Extraia uma árvore desejada de desenvolvimento mais recente que possua o suporte ao UML - para isso, consulte no endereço do projeto: http://user-mode-linux.sourceforge.net/work/current/2.6/ e procure pela última versão (na data em que se escrevia esta parte deste documento - Abril 02 2007 - a última versão com patches disponíveis é a 2.6.21-rc1, portanto extrairemos tal versão).
    $ git-archive --format=tar --prefix=v2.6.21-rc1.uml/ v2.6.21-rc1 | (cd ../ && tar xf -) 
    Isto vai extrair a última árvore candidata a estável (no momento da escrita deste texto) e top de desenvolvimento - gerando o diretório v2.6.21-rc1.uml no diretório abaixo (/usr/src/v2.6.21-rc1.uml).


  • 3) Compile o kernel UML:
  • Limpamos as configurações anteriores (quando existir - no caso de querer 'limpar' a árvore após realizar diferentes configurações e compilar várias vezes, trazendo a árvore para o estado original):
    $ make mrproper 
    $ make mrproper ARCH=um 
    Para gerar um .config apropriado para o uml (o arquivo .config é o que define as configurações do kernel):
    $ make defconfig ARCH=um 
    Então para configurar o kernel, execute a interface gráfica (ncurses) com o comando abaixo. Instale a ncurses-dev (pacote libncurses5-dev) ('apt-get install libncurses5-dev'). Importante: não altere a configuração do tipo do processador no kernel uml (deixe a padrão), e habilite também a opção de compilar o kernel com frame pointers.
    $ make menuconfig ARCH=um 
    E então tente compilar o kernel:
    $ make linux ARCH=um 
    Se obtiver algum erro, veja como corrigir erros ao compilar o kernel no final deste documento - é necessário para a versão atual - 2.6.21-rc1 - que possui um erro, e necessita de uma correção, demonstrada no link.

    E então compile os módulos:

    $ make modules ARCH=um 
    Isso vai gerar os executáveis "linux" e "vmlinux" na raíz do diretório dos fontes do kernel. Pode-se tentar executar o kernel agora, esperando que ele pare a execução com erro quando tentar montar o sistema de arquivos com o comando:
    $ ./vmlinux
    Se obtiver um erro indicando que falta de sistema de arquivos, está tudo ok, não há mais o que configurar para o kernel além do do sistema de arquivos (próxima parte). Mas pode ser que o seguite erro seja obtido:
    PROT_EXEC mmap in /dev/shm/...failed: Operation not permitted
    É devido a restrição de segurança do sistema que não permite código ser executado em memória compartilhada. Tal restrição é resolvida configurando outro arquivo temporário para utilização como temporário: primeiro crie um diretório:
    $ mkdir /tmp/uml
    Depois altere as permissões para o usuário configurado como 'dono' do kernel (execute '$ man chmod' para saber mais) seja o mesmo sob tal diretório (substitua 'user' pelo tal usuário do sistema que está configurado como dono do kernel):
    $ chown user.user /tmp/uml
    Dê permissões total para tal diretório:
    $ chmod 777 /tmp/uml
    E finalmente exporte a variável do sistema que indica o diretório temporário:
    $ export TMPDIR=/tmp/uml


Parte II - Criando e configurando um sistema de arquivos

Agora construímos um sistema de arquivos para uso com o kernel uml. Segue abaixo os procedimentos necessários.

  • 1) Instalando um sistema debian em um diretório
  • Crie um diretório (aqui, nomeado root_fs) e, para manter as coisas relacionadas juntas, criemos tal sistema de arquivos dentro da próprio diretório dos fontes do kernel uml:

    $ cd /usr/src/v2.6.21-rc1.uml/
    $ mkdir root_fs
    Então com o debootstrap instale um sistema debian em tal diretório, executando como root (se você estiver utilizando um sistema debian, instale com '$ apt-get install debootstrap', senão veja como baixar e instalar o deboostrap no final deste documento) com o comando abaixo. Isto vai criar um sistema Debian versão testing (pois no momento, o etch corresponde a versão testing - veja qual é a versão testing no momento no site www.debian.org/releases/) no diretório root_fs/ (de 167Mb). :
    $ debootstrap etch root_fs/ http://ftp.debian.org/debian/

    Caso esteje utilizando um processador AMD-64 em seu computador, substituta o comando acima pelo abaixo:
    $ debootstrap --arch amd64 sarge root_fs/ http://amd64.debian.net/debian-amd64/


  • 2) Gerando um sistema de arquivos em um arquivo e ajustando suas configurações para o uso com o kernel uml
  • O kernel precisa de um __arquivo__ de sistema de arquivos, de onde ele irá acessá-lo. Para isso, primeiro, gere um arquivo com aproximadamente 200MB no diretório do kernel:

     $ cd /usr/src/v2.6.21-rc1.uml/ 
     $ dd if=/dev/zero of=linux_fs.ext3 bs=1M count=200
    Associamos um dispositivo de loopback com o sistema de arquivos do arquivo. Veja quais dispositivos estão disponíveis com o comando '$ losetup -f' (executando como root). A utilização do dispositvo de loopback é necessário para que o layout do sistema de arquivos (suas meta-informações, referentes ao sistema de arquivos), sejem corretamente inseridas no nosso arquivo com o sistema de arquivos:
     $ losetup /dev/loop1 linux_fs.ext3 
    Crie um sistema de arquivos no dispositivo (que foi associado com o arquivo):
     $ mkfs.ext3 -j /dev/loop1 
    Monte o 'sistema de arquivos' do arquivo em um diretório para o acessarmos:
     $ mkdir /mnt/vmfs
     $ mount -t ext3 -o loop linux_fs.ext3 /mnt/vmfs 
    Copie os arquivos da distribuição Debian para o sistema de arquivos criado no arquivo:
     $ cp -dpR root_fs/* /mnt/vmfs/ 
    Agora instalamos os módulos do kernel uml para os diretórios apropriados (/lib/modules/) no sistema de arquivos a ser utilizado por ele com os comandos (sem sair do diretório do kernel uml):
     $ make modules_install INSTALL_MOD_PATH=/mnt/vmfs/ ARCH=um 
    Agora configuramos o sistema a partir de alguns de seus arquivos de configuração. Primeiro edite o arquivo /mnt/vmfs/etc/inittab com um editor de textos, deixando apenas a primeira linha:
    1:2345:respawn:/sbin/getty 38400 tty1
    
    Agora o arquivo /mnt/vmfs/etc/fstab - edite-o de forma a ficar com apenas o conteúdo abaixo:
    #<file system> <mount point>   <type>  <options>     <dump>   <pass>
    /dev/ubd0        /		ext3    defaults        0	0
    /dev/ubd1	none		swap	sw		0	0
    proc            /proc		proc    defaults        0	0 
    Isto indica que os arquivos /dev/ubd{0,1} do sistema principal serão utilizados como dispositivos pelo kernel uml como base para montar o sistema de arquivos a ser utilizado. Tais arquivos podem ser criados automaticamente no momento de sua utilização pelo devfs - no sistema principal que executa o computador, configure a interface udev, configurando os dispositivos ubdX editando o arquivo /etc/udev/links.conf (na distribuição Debian - em outras consulte o manual do udev da sua distribuição: 'man udev'), inserindo as linhas abaixo:
    M ubd0 b 98 0
    M ubd1 b 98 16
    M ubd2 b 98 32
    M ubd3 b 98 48
    M ubd4 b 98 64
    M ubd5 b 98 80
    M ubd6 b 98 96
    M ubd7 b 98 112 
    ou podem também serem criados manualmente se desejar - entrando no diretório que ficam os arquivos associados com dispositivos e criando os arquivos com o comando abaixo:
     $ cd /dev 
     $ for i in 0 1 2 3 4 5 6 7; do mknod ubd$i b 98 $[ $i * 16 ]; done 
    E então crie um arquivo 'swap_file' a ser utilizado como dispositivo de swap do uml (de 200M):
     $ dd if=/dev/zero of=swap_file count=200 bs=1M
     $ mkswap swap_file 
    Finalmente desmontamos o sistema de arquivos destinado para a máquina virtual:
     $ umount /mnt/vmfs 
    Desassociamos ele do dispositivo loopback:
     $ losetup -d /dev/loop1 
    Atribuímos como dono do 'arquivo-sistema-de-arquivos' o mesmo que o do kernel uml (onde 'user' abaixo é o dono do kernel)
     $ chown user.user linux_fs.ext3 
    Deste modo, o arquivo linux_fs.ext3 será o sistema de arquivos.


  • 3) Configurando a rede da máquina virtual uml
  • Para configurar uma conexão de rede na máquina virtual utilizamos o método tun/tap. Tun/tap é uma conexão que associa uma interface com um endereção IP ou ETHERNET a um dispositivo (/dev/net/tun), de modo que quando um processo escreve um pacote para tal endereço da interface este será escrito no dispositivo /dev/net/tun, que entregará o pacote para a manipulação pela camada de rede do kernel do sistema operacional, que irá tratá-lo como se tivesse vindo de tal endereço de rede. Deste modo, tun/tap apresenta-se como um utilitário para comunicação entre processos do espaço do usuário - veja Documentation/networking/tuntap.txt para saber mais. É necessário ter habilitado o suporte a Tun/tap no kernel do sistema principal - veja se está verificando se a opção "CONFIG_TUN=y" no .config deste kernel (caso não esteja, recompile seu kernel com tal opção).

    Será necessário um programa que configure o dispositivo /dev/net/tun em nível de programação - através de invocações IOCTL. Utilizaremos um programa disponível no próprio cvs do projeto uml - tunclt, para isso acesse o cvs e compile o aplicativo:

    $ mkdir /usr/src/uml-project/
    $ cd /usr/src/uml-project/
    $ export CVS_UML=":pserver:anonymous@user-mode-linux.cvs.sourceforge.net:/cvsroot/user-mode-linux"
    $ cvs -d ${CVS_UML} login (sem senha)
    $ cvs -d ${CVS_UML} checkout tools
    $ cd tools/tunctl
    $ make
    

    Teremos 3 interfaces distintas:
    1) Interface eth0 no sistema principal com endereço de rede existente (representando uma rede interna do sistema principal). Para ilustração adoraremos um endereço 192.168.0.1 na rede 192.168.0.[1-7], netmask 255.255.255.248, gateway uma outra interface que possua ip externo (se for o caso de tiver conexão com internet);
    2) Interface eth0 no sistema da máquina virtual com endereço 192.168.1.2 na rede 192.168.1.[1-7], netmask 255.255.255.248, gateway a interface descrita a seguir:
    3) Interface tap0 no sistema principal com endereço 192.168.1.1, na rede 192.168.1.[1-7], netmask 255.255.255.248, gateway a interface acima.

    A interligação ocorre através da interface tap0: quando há a escrita de um pacote na interface eth0 da máquina virtual este é enviado para a interface tun/tap no sistema principal, e vice-versa.

    As configurações são definidas pelo serviço de configuração de rede padrão do debian na sua inicialização, que lê as definições no arquivo /etc/network/interfaces. As interfaces (1) e (3) no sistema principal são definidas da seguinte forma neste arquivo (note: do sistema principal e troque 'user' por algum usuário restrito no sistema):

    #/etc/network/interfaces
    
    auto lo  eth0
    
    iface lo inet loopback
    
    iface eth0 inet static
    	address 192.168.0.1
    	network 192.168.0.248
    	netmask 255.255.255.248
    	broadcast 192.168.0.248
    	gateway 192.168.0.1
    
    iface tap0 inet static
    	address 192.168.1.1 
    	netmask 255.255.255.248
    	pre-up /usr/src/uml-project/tools/tunctl/tunctl -u \ 
    		$( cat /etc/passwd | grep user | cut -f 3 -d :)
    	down /usr/src/uml-project/tools/tunctl/tunctl -d tap /dev/net/tun
    	gateway 192.168.1.2
    
    Dessa forma, quando desejar habilitar a interface tap0 no sistema principal (que deve ser feito antes de iniciar a execução de uma máquina virtual), execute "ifup tap0", que então este arquivo será lido e as definições serão atribuidas e os comandos precedidos por 'up/down/pre-up' serão executados apropriadamente.

    No sistema da máquina virtual, monte seu sistema de arquivos e configure a interface (2) editando seu arquivo de configuração (/mnt/vmfs/etc/network/interfaces) definido como segue:

     $ mount -t ext3 -o loop linux_fs.ext3 /mnt/vmfs 
    #/etc/network/interfaces - (uml virtual machine "vm001")
    auto lo  eth0
    
    iface lo inet loopback
    
    iface eth0 inet static
            address 192.168.1.2
            network 192.168.1.248
            netmask 255.255.255.248
            broadcast 192.168.1.248
            gateway 192.168.1.1
    
    Ajuste também o arquivo /etc/hosts da máquina virtual (em /mnt/vmfs/etc/hosts):
    #/etc/hosts - network configuration (uml virtual machine "vm001")
    127.0.0.1	localhost
    192.168.1.2	vm001
    192.168.0.1     motherhost
    
    Finalizando com o hostname (neste caso, a máquina virtual recebe o nome de "vm001"):
    $ echo "vm001" > /mnt/vmfs/etc/hostname
    
    Finalmente desmontamos o sistema de arquivos destinado para a máquina virtual:
     $ umount /mnt/vmfs 

    Se desejar liberar o acesso da máquina virtual com endereço na rede interna de 192.168.1.2 para a internet através da rede no sistema principal (considerando que o sistema principal tenha acesso a internet), utilize o iptables para criar uma tabela de tradução de endereços rede (NAT) no sistema principal:

     $ echo 1 > /proc/sys/net/ipv4/ip_forward
     $ iptables -t nat -A POSTROUTING -s 192.168.1.2 -d ! 192.168.0.0/29 -o eth0 -j MASQUERADE
     $ iptables -I FORWARD -s 192.168.1.2 -m state --state ! INVALID -j ACCEPT
    
    E se desejar acessá-la a partir da internet (e.g. serviço ssh na porta 22), abra uma porta no sistema principal (e.g. 1020) e redirecione conexões com destino a esta porta chegando da internet para a máquina virtual na porta 22 (altere IP_EXTERNO apropriadamente):
     $ iptables -t nat -I PREROUTING -d ${IP_EXTERNO} -p tcp --dport 1020 -j DNAT --to 192.168.1.2:22
     $ iptables -I FORWARD -p tcp -d 192.168.1.2 --dport 22 -j ACCEPT
    


Parte III - A máquina virtual: a utilização e o ambiente para o desenvolvimento e depuração do kernel do Linux

Nesta parte descreve-se os procedimentos para utilização da Máquina Virtual obtida com o kernel UML e o sistema de arquivos construído.
Tal Máquina Virtual tem sido muito utilizada também por desenvolvedores do kernel do Linux, para testar alterações no kernel da Máquina Virtual, o que de outro modo (quando utilizado como kernel do sistema operacional inicial) faria com que as falhas no kernel sendo testado causasse a parada do sistema e a necessidade de reinício do computador e de todo o sistema a cada falha encontrada no kernel em teste. Além disso, com o kernel UML é possível ainda depurá-lo, e então acompanhar o fluxo de execução do sistema operacional Linux :).

  • 1) Utilização da máquina virtual UML:
  • Agora que o kernel uml e o sistema de arquivos estão criados, habilitamos a interface tap0 no sistema principal para estar preparado para a conexão com a rede da máquina virtual:

     $ ifup tap0 
    E finalmente executamos o kernel e iniciamos a máquina virtual com o comando:
     $ ./linux  ubd0=linux_fs.ext3 ubd1=swap_file mem=128M con0=fd:0,fd:1 \
    		con=xterm ssl=xterm eth0=tuntap,tap0 umid=vm001 
    A barra invertida no final da primeira linha do comando indica apenas a continuação do comando na linha de baixo. Caso tenha criado os arquivos /dev/ubdX no sistema principal manualmente, informe para desativar o uso do devfs inserindo a string 'devfs=none' no comando de inicialização acima.

    E então deverá ocorrer a inicialização da máquina virtual exibindo as mesmas informações que em uma inicialização normal de um kernel linux em um computador, e uma janela xterm irá se abrir com o prompt do login ao sistema. Acesse como usuário root, a senha não é necessária - configure se desejar com o comando 'passwd'.

    O kernel do Fedora Core 5 tem apresentado um problema quando na execução do kernel uml - congelando este, logo após a montagem do sistema de arquivos. Se este for o caso, crie o seu próprio kernel para o sistema principal, e então tente executar novamente o kernel uml.

    Outro problema que pode-se encontrar é quando a versão do kernel principal (sendo executado no computador, e não o uml) é inferior a 2.6.16-rc6 - neste caso a seguinte mensagem de erro é obtida quando na execução do kernel: "Kernel panic - not syncing: handle_trap - failed to wait at end of syscall, errno = 0, status = 2943". Neste caso, compile um kernel para o seu computador mais recente que a versão 2.6.16-rc6.

    Para finalizar a máquina virtual, desligue o sistema:

     $ shutdown -h now 


  • 2) Ambiente para desenvolvimento do kernel do linux
  • Há 3 modos principais para realizar alterações no kernel do Linux:

    • A) Criando/alterando código do kernel diretamente em algum de seus arquivos fonte
    • Inicie o desenvolvimento, alterações ou aplique patchs para teste nos códigos do kernel do linux UML. Após as alterações, compile-o novamente:

      $ make linux ARCH=um 

    • B) Criando/alterando código de algum módulo em algum de seus arquivos fonte
    • Se estiver modificando algum módulo, é necessário realizar os procedimentos seguintes: compilar os módulos, montar o sistema de arquivos utilizado pelo uml novamente e então copiar os módulos para o sistema de arquivos, e finalmente desmontá-lo:

       $ make modules ARCH=um
       $ mount -t ext3 -o loop linux_fs.ext3 /mnt/vmfs/
       $ make modules_install INSTALL_MOD_PATH=/mnt/vmfs/ ARCH=um
       $ umount /mnt/vmfs/ 

    • C) Criando código para construção de um módulo externo aos arquivos fonte do kernel
    • Crie um diretório em algum lugar desejado (e.g.: ~/devel/my_modules), e então inicie criando a partir de um arquivo fonte inicial, o desenvolvimento do módulo. Temos como exemplo o arquivo "procinfo.c": utilizado para construir um módulo externo que na sua inserção imprime informações dos processos em execução no sistema.

      Para este caso, há ainda a preocupação para que tipo de kernel o módulo a ser construído deva ser utilizado. Para compilar o código fonte, e automatizar o processo, crie um arquivo Makefile para compilar o módulo, conforme abaixo (alterando o valor da variável KERNELDIR de acordo com a localização dos fontes da versão do kernel a que deseja-se que o módulo seja utilizado - é necessário ter disponível os fontes de tal kernel):

      # Makefile 
      
      obj-m := procinfo.o
      module-objs := procinfo.o
      
      KERNELDIR = /usr/src/v2.6.21-rc1.uml/
      
      default:
              make -C $(KERNELDIR) M=$(shell pwd) modules ARCH=um
      
      clean:
              rm -f  rm -f procinfo.o procinfo.ko procinfo.mod.c procinfo.mod.o Module.symvers
      Então compile:
       $ make 
      Então é necessário montar o sistema de arquivos utilizado pelo kernel UML novamente para copiar o módulo criado afim de que possa ser encontrado no sistema de arquivos da Máquina Virtual quando executado, e depois finalmente desmontá-lo:
      $ mount -t ext3 -o loop linux_fs.ext3 /mnt/vmfs/
      $ cp /caminho/para/modulo/criado/procinfo.ko /mnt/vmfs/usr/src/
      $ umount /mnt/vmfs/ 
      Após reiniciar o sistema com os devidos parâmetros (descrito no início desta parte), faça a inserção do módulo na Máquina Virtual com o comando "insmod procinfo.ko", e a remoção com "rmmod procinfo". E então teste novamente a máquina virtual tentando executá-la conforme descrito no inínicio desta parte. Se por qualquer motivo sua máquina virtual congelar, certifique-se de que não há instâncias da máquina virtual executando pois o sistema irá reportar erro de recurso indisponível temporariamente (errno 11). Mate os processos anteriores sempre que ficar alguma instância residente após algum tipo de reinício devido a quebras no sistema:
       $ pkill -9 linux 


  • 3) Ambiente para depuração do kernel do linux
  • Caso venha a obter erros, ou deseje depurar o fluxo de execução do kernel, você pode utilizar o GNU gdb. Para depurar módulos é necessário alguns procedimentos adicionais, então descritos em secções separadas:

    • A) Depuração do kernel
    • Inicie o kernel uml a partir do gdb:

       $ gdb ./linux 
      É necessário informar o depurador para ignorar pelos sinais recebidos SIGUSR1 e SIGSEGV:
       (gdb) handle SIGSEGV pass nostop noprint 
       (gdb) handle SIGUSR1 pass nostop noprint 
      Agora indicamos um ponto de parada (break point) na função 'start_kernel' do kernel:
       (gdb) b start_kernel 
      E então iniciamos a execução do kernel no depurador GNU:
       (gdb) r ubd0=linux_fs.ext3 ubd1=swap_file mem=128M con0=fd:0,fd:1 \
      				con=xterm ssl=xterm eth0=tuntap,tap0 umid=vm001
      Se desejar parar a execução do kernel no gdb, um <CTRL-C> irá acabar por encerrar o próprio gdb devido ao modo do terminal RAW, portanto o método a ser utilizado é abrir um outro terminal (xterm) e envie um sinal SIGINT (solicitando uma interrupção na execução) para a thread principal do kernel uml executando (troque "$(whoami)" pelo usuário que executa o uml se estiver utilizando algum esquema de proteção sgid,suid, etc.):
       $  kill -INT $(cat /home/$(whoami)/.uml/vm001/pid) 
      E agora podemos dar procedimento na depuração no gdb - veja logo abaixo exemplos de comando. Desejando continuar a execução do kernel, execute 'c' (continue) no depurador novamente. Novamente, mate os processos anteriores sempre que ficar alguma instância residente após algum tipo de reinício devido a quebras no sistema:
       $ pkill -9 linux gdb port-helper 


    • B) Depuração de módulos
    • Para depurar um módulo logo quando este é carregado e acompanhar sua execução é necessário adicionar os símbolos do módulo na tabela de símbolos do depurador logo no momento em que este já esteje alocado no kernel uml e armazenado em um ponteiro global 'modules' que representa a lista dos módulos carregados internamente no kernel, além de indicar a posição da alocação (o endereço de memória). Para agilizar este procedimento e torná-lo automático, utilizamos 3 arquivos (que devem ser alterados apropriadamente de acordo com a localização dos módulos, kernel e sistema de arquivos utilizado):

      1) umlgdb_dispatcher: inicia um xterm executando o kernel no gdb e controla a parada no depurador;
      2) umlgdb_wrapper: carrega os símbolos do módulo automaticamente logo que inserido com 'insmod' na máquina virtual e pára a execução do kernel no depurador no local da chamada a função init do módulo). Tal arquivo foi construído baseado do original 'umlgdb' disponibilizado no diretório tools do projeto UML por Chandan Kudige;
      3) .gdbinit: a ser colocado no diretório do kernel uml (/usr/src/v2.6.21-rc1.uml se seguido a parte I deste documento), será lido e executado automaticamente pelo depurador gdb sempre que este for executado neste diretório. Para iniciar a execução do uml sobre o gdb, dê permissão de execução para o arquivo umlgdb_dispatcher e execute-o:

       $ chmod +x umlgdb_dispatcher 
       $ ./umlgdb_dispatcher 
      O que acontece é que o umlgdb_dispatcher abre um xterm para iniciar a execução do kernel uml sobre o gdb, e quando detecta um 'insmod ' na máquina virtual, o umlgdb_wrapper automaticamente carrega a tabela de símbolos do módulo no endereço de memória em que fora carregado. Pressionando <ENTER> no terminal do umlgdb_dispatcher este interrompe a execução do kernel uml e devolve o controle para o depurador. Pressionando <CTRL-C> o umlgdb_dispatcher automaticamente limpa o ambiente matando os processos envolvidos evitando deixá-los como zombie. Uma tela demonstrando a execução.


Alguns comandos do depurador GNU gdb:

  Inserir um ponto de parada em uma linha de um arquivo .................[b]reak x.c:105
  Inserir um ponto de parada em uma funcao ..............................[b]reak some_function
  Inserir um ponto de parada temporario em uma funcao ...................tbreak another_function
  Listar pontos de paradas existentes ...................................[i]nfo [b]reakpoints
  Desabilitar um ponto de parada (o 2ndo listado) .......................disable 2
  Nao pare no 2ndo ponto de parada nas proximas 8 vezes .................ignore 2 8
  Apague 2ndo ponto de parada ...........................................[d]elete 2
  Execute a linha atual do programa e pare na proxima execucao ..........[s]tep
  Se o programa atual contem uma chamada de funcao execute-a e pare .....[n]ext
  Executa 'next' para todas as funcoes invocadas ate o final da funcao ..finish
  Continue a execucao ate o proximo ponto de parada .....................[c]continue
  Lista 10 ou mais linhas de codigo ao redor da linha atual .............[l]ist
  Sair do GDB ...........................................................[q]uit 

Para mais informações sobre depuração, e kernel uml no gdb, veja:


Parte IV - Soluções para eventuais problemas

  • Instalando o Debootstrap em sistemas que não o Debian

  • O programa debootstrap é parte do sistema Debian, é um conjunto de scripts shell que utilizam a glibc (e, portanto, deve estar instalada), e o método para instala-lo em outros sistemas é como segue:
    • 1) Copie o pacote para o diretório /usr/local/src o binário do debootstrap dos servidores Debian
    •  $ cd /usr/local/src/ 
       $ wget http://ftp.debian.org/debian/pool/main/d/debootstrap/debootstrap-udeb_0.3.3_i386.udeb 
    • 2) Descompacte os arquivos binários a partir deste ('ar' á um dos binários do conjunto que compõem o pacote 'binutils').
    •  $ ar -x debootstrap-udeb_0.3.3_i386.udeb 
    • 3) Instale copiando os executáveis para os diretórios padrões do sistema (/usr/bin). Você precisará ter privilégios de root para instalar estes binários.
    •  $ cd / 
       $ zcat /usr/local/src/work/data.tar.gz | tar xv 


  • Erro ao compilar o kernel UML

  • Esta secção descreve como resolver erros no momento de compilar o kernel. Nesta versão utilizada (2.6.21-rc1) obteve-se a seguinte mensagem de erro:
    [...]
    CC      mm/slab.o
    mm/slab.c:3557: error: conflicting types for ‘kmem_ptr_validate’
    include/linux/slab.h:58: error: previous declaration of ‘kmem_ptr_validate’ was here
    make[1]: ** [mm/slab.o] Erro 1
    make: ** [mm] Erro 2
    $
    
    Portanto é necessário verificar se tal erro já fora corrigido, verificando nos patches. Para isso então, vamos baixar o arquivo patches.tar que contém todos os patches já migrados para a versão escolhida (2.6.20-rc1) no endereço dos patches disponíveis:
    $ wget http://user-mode-linux.sourceforge.net/work/current/2.6/2.6.20-rc1/patches.tar 
    
    Extraímos os patches em um diretório patches/ a ser gerado na extração dos patches:
    $ tar xv patches.tar 
    
    Entramos no diretório e então verificamos se há algum patch que contenha alguma correção para o arquivo indicado com erro no momento de compilar o kernel - neste caso, o erro é em slab.c - com o seguinte comando:
     $ cd patches/
     $ grep -n Index: *  | grep slab.c
    fastcall:4:Index: linux-2.6.17/mm/slab.c
    
    Verificando o arquivo na linha que indicada, e analisando o erro, nota-se que este patch (arquivo "fastcall") refere-se a correção do erro apontado (mm/slab.c:3557: error: conflicting types for #kmem_ptr_validate#):
    $  cat patches/fastcall 
    # There's a mismatch between the definition and declaration of
    # kmem_ptr_validate.  This removes the "fastcall" from the definition.
    
    Index: linux-2.6.17/mm/slab.c
    ===================================================================
    --- linux-2.6.17.orig/mm/slab.c 2006-12-14 23:01:47.000000000 -0500
    +++ linux-2.6.17/mm/slab.c      2006-12-14 23:07:00.000000000 -0500
    @@ -3553,7 +3553,7 @@ EXPORT_SYMBOL(kmem_cache_zalloc);
      *
      * Currently only used for dentry validation.
      */
    -int fastcall kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr)
    +int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr)
     {
            unsigned long addr = (unsigned long)ptr;
            unsigned long min_addr = PAGE_OFFSET;
    $
    
    Então aplicamos o patch a partir do diretório dos fontes do kernel com o comando a seguir:
    $ cd /usr/src/v2.6.21-rc1.uml
    
    $ cat patches/fastcall | patch -p1
    
    E então tenta-se novamente continuar a compilar o kernel, re-executando o comando:
    $ make linux ARCH=um 
    
    Caso obtenha outros erros, faça a mesma verificação do erro nos patches para ver se há alguma correção já disponível, e então aplique o patch conforme os procedimentos acima, e continue a compilar o kernel até obter sucesso.