Criar uma distribuição Linux integrada a partir do zero

Aprenda como construir uma distribuição Linux® customizada para utilizar em um ambiente integrado, neste caso para conduzir um computador single-board Technologic Systems TS-7800. Neste tutorial, você aprende sobre cross-compiling, o boot loader, sistemas de arquivos, o sistema de arquivos raiz, imagens de disco e o processo de boot, tudo relacionado às decisões tomadas quando você está construindo o sistema e criando a distribuição.

Peter Seebach, Author, 自由职业者

Peter SeebachPeter Seebach coleciona dispositivos pequenos que executam em Linux. Ele está cansado de ouvir piadas sobre criar um cluster Beowulf a partir deles.



12/Ago/2008

Antes de Iniciar

Objetivos

Este tutorial mostra como instalar o Linux em um sistema de destino. Não uma distribuição Linux pré-construída, mas a sua própria, construída a partir do zero. Embora os detalhes do procedimento necessariamente variem de um destino para outro, os mesmos princípios gerais se aplicam.

O resultado deste tutorial (se você tiver um objetivo apropriado) é um sistema Linux funcional no qual pode obter uma linha de comando do shell.

Sobre este tutorial

O tutorial inicia com uma discussão dos problemas de cross-compilation, então, discute quais são os componentes de um sistema Linux e como eles são reunidos. Tanto a construção, como a instalação e a configuração do sistema de destino são abordadas.

O destino específico discutido, um Technologic Systems TS-7800, impõe seus próprios comportamentos padrão de boot e bring-up; outros sistemas terão outra mecânica e este tutorial não entra em grandes detalhes sobre todos os boot loaders possíveis.

Pré-requisitos e requisitos do sistema

Desenvolvedores que estão interessados na escolha de sistemas embarcados ou que apenas querem aprender mais sobre como os sistemas Linux são em sua essência, obterão o máximo deste tutorial.

O ambiente host utilizado é o Ubuntu, mas outros sistemas também funcionam. Assume-se que os usuários tenham familiaridade básica com os problemas de administração do sistema UNIX® ou Linux. O tutorial assume acesso do root a um sistema host.

Este tutorial assume que o seu shell é um Bourne shell derivativo; se você utilizar um shell C derivativo, o prompt provavelmente terá uma aparência diferente e você precisará utilizar comandos diferentes para configurar variáveis de ambiente.

Para cross-compiling (que é útil ao escolher sistemas embarcados), eu utilizei o crosstool-ng versão 1.1.0, lançado em maio de 2008. Você pode fazer download dele a partir do site de distribuição (consulte Recursos). Detalhes podem ser localizados na seção instalando e configurando-o.


Sobre o Destino e a Arquitetura

Destino

O destino que eu escolhi foi o Technologic Systems TS-7800 (consulte Recursos para obter mais detalhes). Este é um pequeno sistema ARM integrado, com armazenamento de atualização integrado e removível, bem como um controlador SATA. Este tutorial o orienta pela configuração deste sistema para inicializar um prompt de login, sem contar com binários pré-construídos.

Arquitetura

Eu escolhi a arquitetura ARM para tornar mais fácil verificar se um determinado binário é host ou de destino e para tornar fácil visualizar quando pode estar ocorrendo poluição de host. Também é apropriado ter uma máquina que consuma um total de cerca de 5 W de energia e execute totalmente de maneira silenciosa.


Cross-compilation

O que é cross-compiling?

Cross-compiling é utilizar um compilador em um sistema para desenvolver código para executar em outro. A cross-compiling é relativamente rara entre usuários ocasionais do UNIX, já que o padrão é ter um compilador instalado para uso em tal sistema. Entretanto, a cross-compiling torna-se bastante comum (e relevante) ao escolher sistemas embarcados. Mesmo quando o host e o destino estão na mesma arquitetura, é necessário distinguir entre seus compiladores; eles podem ter diferentes versões de bibliotecas ou bibliotecas construídas com diferentes opções de compilador, por isso, algo compilado com o compilador do host poderia falhar ao executar ou poderia comportar-se de maneira inesperada no destino.

Obtendo ferramentas de compilação cruzada

Ou seja, na teoria, é bem possível construir um compilador cruzado sozinho, mas isto é raramente prático. A série de estágios de autoinicialização necessária pode ser difícil e consumir tempo e sempre é necessário construir um compilador mínimo, que é utilizado para parcialmente configurar e construir bibliotecas, os cabeçalhos dos quais são, então, utilizados para reconstruir o compilador para que possa utilizá-los e assim por diante. Uma grande quantidade de fontes comerciais para trabalhar com compiladores cruzados para diversas combinações de arquitetura estão disponíveis, bem como diversos kits de ferramentas de cross-compiling gratuitos.

Apresentando o crosstool-ng

O crosstool de Dan Kegel (consulte Recursos para obter detalhes) coleta uma variedade de conhecimentos e algumas correções especializadas para automaticamente construir cadeias de ferramentas para uma grande quantidade de sistemas. O crosstool não tem sido atualizado faz algum tempo, mas o novo projeto crosstool-ng é baseado neste trabalho. Para este tutorial, eu utilizei o crosstool-ng versão 1.1.0, lançado em maio de 2008. Faça download dele a partir do site de distribuição (consulte Recursos).

Instalando o crosstool-ng

O crosstool-ng tem um script de configuração . Para configurá-lo, simplesmente execute o script utilizando --prefix para configurar um local. Por exemplo:

$ ./configure --prefix=$HOME/7800/ctng

Assim que o tiver configurado, construa-o utilizando make e, então, make install. O processo de construção cria um diretório ctng no diretório de trabalho do 7800 que contém os scripts de construção do crosstool-ng. Inclua o subdiretório ctng/bin em seu caminho:

$ PATH=$PATH:$HOME/7800/ctng/bin

Configurando o crosstool-ng

O crosstool-ng utiliza um arquivo .config semelhante àqueles utilizados pelo kernel Linux. É preciso criar um arquivo de configuração que corresponde ao seu destino para utilizar o crosstool-ng. Crie um diretório de trabalho para uma construção crosstool-ng:

$ mkdir toolchain-build
$ cd toolchain-build

Agora, copie em uma configuração padrão. É possível configurar o crosstool-ng manualmente, mas uma das configurações de amostra ajusta-se ao destino perfeitamente:

$ cp ../ctng/lib/ct-ng-1.1.0/samples/arm-unknown-linux-uclibc/* .

Finalmente, renomeie o arquivo crosstool.config :

$ mv crosstool.config .config

Isto copia em um arquivo de configuração que tem como destino um processador armv5te, o modelo utilizado no TS-7800. Ele é construído com uClibc, uma variante libc destinada a sistemas embarcados. Entretanto, o arquivo de configuração não precisa de uma modificação.

Corrigindo o caminho da configuração

O diretório de destino padrão para uma construção do crosstool-ng é $HOME/x-tools/$TARGET. Por exemplo, nesta construção, ele poderia aparecer como x-tools/arm-unknown-linux-uclibc. Isto é muito útil se você estiver construindo para uma grande quantidade de destinos, mas não é tão útil se você estiver construindo para apenas um destino. Edite o arquivo .config e altere CT_PREFIX_DIR para ${HOME}/7800/toolchain.

Construindo a string de ferramentas

Para construir a string de ferramentas, execute o script ct-ng com o argumento build .Para melhorar o desempenho, especialmente em um sistema com vários núcleos, você pode querer executar com várias tarefas, especificadas como build.#. Por exemplo, este comando é construído com quatro tarefas:

$ ct-ng build.4

Isto pode levar algum tempo, dependendo do seu sistema host. Quando isto tiver sido concluído, a string de ferramentas está instalada em $HOME/7800/toolchain. O diretório e seus conteúdos são marcados como somente leitura; se você precisar movê-los ou excluí-los, utilize o chmod u+w. O script ct-ng obtém outros argumentos, tal como help. Observe que ct-ng é um script para o utilitário make padrão e, como resultado, a saída de --help é apenas a ajuda do make padrão; utilize ct-ng help para obter ajuda para o crosstool-ng.

Se você não tiver visto este truque antes, ele é genial. Sistemas UNIX modernos interpretam um arquivo executável no qual a primeira linha inicia com #! como um script, especificamente, um script para o programa nomeado no resto da linha. Por exemplo, muitos shell scripts iniciam com #!/bin/sh. O nome do arquivo é transmitido para o programa. Para programas que tratam seu primeiro argumento como um script para executar, isto é suficiente. Embora make não faça isto automaticamente, você pode fornecer a ele um arquivo para ser executado utilizando o sinalizador -f . A primeira linha do ct-ng é #!/usr/bin/make -rf. O sinalizador -r suprime as regras de construção padrão integradas de make e o sinalizador -f informa que a palavra seguinte (que é o nome do arquivo do script) é o nome de um arquivo para utilizar ao invés de um Makefile. O resultado é um script executável que utiliza a sintaxe make ao invés da sintaxe do shell.

Utilizando a string de ferramentas

Para iniciantes, inclua o diretório contendo o compilador em seu caminho:

$ PATH=~/7800/toolchain/bin:$PATH

Com isso em seu caminho, agora você pode compilar programas:

$ arm-unknown-linux-uclibc-gcc -o hello hello.c $ file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for
GNU/Linux 2.4.17, dynamically linked (uses shared libs), not stripped

Onde estão as bibliotecas?

As bibliotecas utilizadas pela string de ferramentas para vincular binários estão armazenadas em arm-unknown-linux-uclibc/sys-root, sob o diretório toolchain . Este diretório forma a base de um eventual sistema de arquivos raiz, um tópico que iremos abranger sob Sistemas de arquivos, assim que o kernel for construído.

Configuração de kernel

A árvore de distribuição do kernel fornecida pelo fornecedor já está configurada para cross-compiling. No caso mais simples (que é este), a única coisa que você precisa fazer para executar uma cross-compiling de um kernel Linux é configurar a variável CROSS_COMPILE no Makefile de nível superior. Este é um prefixo que é anexado no início de diversos programas (gcc, as, ld) utilizado durante a construção. Por exemplo, se você configurar CROSS_COMPILE para arm-, a compilação tentará localizar um programa denominado arm-gcc em seu caminho. Para esta construção, então, o valor correto é arm-unknown-linux-uclibc. Ou, se você não quiser contar com as configurações de caminho, é possível especificar o caminho inteiro, como neste exemplo:

CROSS_COMPILE ?= $(HOME)/7800/toolchain/bin/arm-unknown-linux-uclibc-


Construindo o Kernel

Fazendo download da origem

Faça download dos arquivos de origem Linux e de configuração do TS-7800 da Technologic e descompacte os arquivos zip em um local apropriado.

Configuração de kernel

Uma discussão completa sobre a configuração do kernel está além do escopo deste tutorial. Neste caso, o destino ts7800_defconfig me deu uma configuração padrão utilizável para o 7800, com um pequeno contratempo: a configuração CONFIG_DMA_ENGINE foi terminada com off quando deveria ter sido terminada com on.

Refinando o kernel

Normalmente, é melhor editar o kernel utilizando make menuconfig, que oferece uma interface semigráfica para a configuração do kernel. Esta interface é navegada utilizando as teclas de seta para mover o cursor, a tecla Tab para selecionar opções na parte inferior da tela e as teclas de espaço ou Enter para selecionar opções. Por exemplo, para sair sem alterar nada, pressione Tab até que a opção <Sair> na parte inferior da tela fique realçado e, então, pressione Enter. Executar make menuconfig novamente reabre o editor.

Alterando o console padrão

O TS-7800 normalmente inicializa silenciosamente, porque a configuração padrão do kernel especifica um dispositivo de console nulo para manter o monitor silencioso. Para alterar isto, utilize as teclas de seta para navegar para baixo para as "Opções de boot" e pressione Enter. A terceira linha mostra as opções padrão do kernel, que seleciona o ramdisk, o script de inicialização e o console. Utilize as teclas de seta para navegar para baixo até esta linha, pressione Enter e altere console none para console ttyS0,115200. Em seguida, pressione Tab para mover o cursor para <Ok> na parte inferior do painel e pressione Enter. Agora, pressione Tab para selecionar <Sair> e pressione Enter, retornando-o ao menu principal.

Com o objetivo de inicializar o mais rápido possível, o console não é útil e, de fato, mesmo em uma alta taxa de bauds, o envio de mensagens de kernel pode levar uma fração notável de tempo que o sistema leva para inicializar. Entretanto, para depuração e reprodução, você precisa do console.

Ativando o mecanismo DMA

Navegue para baixo até "Drivers de dispositivo" e pressione Enter. Esta lista é mais longa do que o monitor usual, portanto, você precisará rolar até o final da opção para "Mecanismos DMA". Navegue até esta opção com as teclas de seta e pressione Enter. Há duas opções na parte superior desta página que possuem colchetes indicando uma opção booleana. A segunda opção, "Suporte para mecanismos DMA", não estava ativada por padrão no download com o qual eu iniciei. Navegue até esta opção com as teclas de seta e pressione a tecla de espaço para alternar seu estado. Agora, utilize Tab e Enter para selecionar <Sair> em cada tela para navegar até o nível superior do programa e, então, <Sair> mais uma vez para sair do programa. Quando perguntado se você deseja salvar sua nova configuração de kernel, utilize a tecla Tab até chegar em <Sim> e pressione Enter.

Compilando o kernel

Digite make. Sim, é realmente simples assim. Isto constrói um kernel, bem como uma coleção de módulos. Novamente, usuários com vários núcleos podem desejar várias tarefas; tente make -j 5. Para os propósitos deste projeto, vou ignorar os módulos de kernel e favorecer a compilação de quaisquer recursos necessários. A técnica ramdisk de autoinicialização utilizada para obter os drivers necessários no kernel antecipadamente parece excessiva e a construção de um sistema de arquivos raiz já é complicada o suficiente. Isto, é claro, traz a questão de como obter uma inicialização de kernel, o assunto da próxima seção.


Boot Loaders

O que é um boot loader?

Um boot loader (ou loader de autoinicialização) é um programa que carrega outro programa. O boot loader é uma parte pequena e especializada do código, específico para um sistema de destino, que possui apenas a complexidade suficiente para localizar o kernel e carregá-lo sem ser um kernel com funções completas. Diferentes sistemas utilizam uma variedade de diferentes boot loaders, desde programas BIOS enormes e complicados comuns em PCs desktop, até programas muito pequenos e simples mais comuns em sistemas embarcados.

Um boot loader simples

O TS-7800 utiliza um boot loader surpreendentemente simples, que simplesmente seleciona o kernel a partir de uma partição predeterminada da placa SD ou atualização integrada. Um jumper determina se a placa procura primeiro na atualização integrada ou nas placas SD. Não há outras definições de configuração. Boot loaders mais complicados (tal como grub, comumente utilizados em PCs desktop) possuem opções para configurações de kernel e assim por diante no momento do boot. Neste sistema, as opções padrão do kernel devem ser compiladas, conforme descrito anteriormente.

A decisão de compilar as opções de kernel é um exemplo típico de uma opção que faz algum sentido em um sistema embarcado, mas seria desconfortavelmente limitada em um desktop.

Formatos de kernel

Há uma variedade de formatos nos quais os kernels são armazenados. O binário de kernel Linux inicial, denominado vmlinux, é raramente o arquivo com o qual um boot loader trabalhará. No TS-7800, o boot loader pode utilizar dois arquivos, Image (descompactado) ou zImage (compactado). Estes arquivos são criados no diretório arch/arm/boot dentro da árvore do kernel.

As pessoas sempre descrevem o kernel zImage como um kernel compactado e então esperam que o boot loader precise fornecer descompactação. De fato, isto é muito mais engenhoso. O kernel zImage é um executável descompactado, que contém um objeto de dados estático particularmente grande que é uma imagem de kernel compactada. Quando o boot loader carrega e executa o executável zImage , tal executável então descompacta a imagem do kernel e a executa. Desta maneira, você obtém o máximo do benefício da compactação sem impor esforços adicionais no boot loader.

Configurando a placa SD

A placa SD precisa de uma tabela MBR contendo informações de partição do estilo DOS. Uma tabela MBR de amostra está disponível para download a partir do site da Technologic; isto é para uma placa de 512 MB, mas é fácil editar a quarta partição para um tamanho adequado a qualquer tamanho de placa que deseja utilizar. As primeiras três partições são de 4 MB cada; a primeira não é utilizada em placas menores e a segunda e a terceira contêm o kernel e a imagem ramdisk inicial, respectivamente.

Para meus propósitos, utilizei o utilitário sfdisk , o que nem sempre é recomendado mas, possui a peculiaridade desejável de confiança quando eu solicito a criação de uma partição que não está alinhada em um limite de partição.

Instalando o kernel inicial

O kernel para o TS-7800 é descarregado diretamente na segunda partição da placa SD. O caminho exato para a placa depende de como você a esta acessando; normalmente se você tiver uma placa em um leitor de placa USB, ela será detectada como um dispositivo SCSI. Observe que não há acesso ao sistema de arquivos envolvido; o kernel bruto é simplesmente descarregado na partição. O comando dd copia dados brutos de uma origem para outro, como neste exemplo:

$ dd if=zImage of=/dev/sdd2 bs=16k
93+1 records in
93+1 records out
1536688 bytes (1.5 MB) copied, 0.847047 s, 1.8 MB/s

Este comando descarrega dados brutos a partir do arquivo zImage para a segunda partição do /dev/sdd, utilizando blocos de 16 KB.

Um pouco sobre tamanhos de blocos

A saída deste comando é um pouco enigmática (para ser honesto, assim como suas entradas também são). Quando dd é executado, ele copia dados nos "registros", que são, por padrão, blocos de 512 bytes; este comando especifica um tamanho de bloco de 16 k (que dd compreende como 16*1024).

O comando dd relata a quantidade de dados copiados primeiro nos blocos; o número após o sinal de mais é o número de blocos parciais copiados. Neste caso, como 1.536.688 não é um múltiplo exato do tamanho do bloco, os bytes restantes do arquivo são lidos (e gravados) separadamente como um bloco parcial. Estas informações são inofensivas para a maioria dos dispositivos modernos, mas são cruciais para diagnosticar problemas com alguns dispositivos mais antigos.

A habilidade de controlar tamanhos de blocos (e recolocar dados em bloco ao transferi-los) era excepcionalmente útil para trabalho com dispositivos de fita e outras mídias especializadas que exigiam gravações com tamanhos fixos específicos e também ajuda por motivos de desempenho e confiabilidade com dispositivos de atualização.

Embora o dispositivo do kernel representando mídias de atualização possa frequentemente executar gravações de tamanhos arbitrários, é comum para o dispositivo subjacente ao trabalho apenas em blocos integrais, frequentemente de tamanhos um pouco maiores (4 KB ou maiores). Para executar uma gravação parcial, o dispositivo de atualização deve extrair os conteúdos do bloco integral, modifique-os com os dados de entrada e atualize o bloco inteiro. Se um dispositivo utiliza blocos de 4 KB e você gravar neles em blocos de 512 bytes, cada bloco de dispositivo é regravado oito vezes para uma única cópia. Isto é inválido para a longevidade do dispositivo e também inválido para o desempenho. (Uma gravação de 512 bytes do mesmo arquivo foi metade do tempo mais rápida na placa de atualização que eu utilizei).

Executando o boot no kernel

Executar o boot no kernel é bastante simples. Configure o jumper para um boot SD, coloque a placa no sistema e ligue-o. Se você iniciou com uma placa em branco, isto produz um resultado previsível:

Kernel panic - sem sincronia: VFS: Não é possível montar root fs em unknown-block(1,0)

Esta mensagem enigmática indica que o kernel não pôde localizar seu sistema de arquivos de raiz. Isto significa que é o momento de criar um.


Sistemas de arquivos

Sistemas de arquivos de raiz

Normalmente, o Linux possui apenas um sistema de arquivos de raiz. Entretanto, durante o boot, é comum utilizar um sistema de arquivos de raiz temporário para carregar e configurar drivers de dispositivo e assim por diante. O TS-7800 utilize este sistema arquivos temporário (denominado um ramdisk inicial ou initrd) para o driver SD e para uma variedade de opções de configuração. De fato, o ramdisk é utilizado para muitas coisas (tal como a determinação do sistema de arquivos de raiz final a utilizar) que pode ser identificado pelo boot loader em outro sistema.

Manipulando sistemas de arquivos

Uma advertência geral é aplicável: a maioria das operações de sistemas de arquivos requer privilégios de administrador. Há algumas exceções e algumas soluções alternativas. A mais notável é o utilitário/a biblioteca fakeroot que permite que você crie aplicativos com "suporte a fakeroot". Isto possibilita a manipulação de um sistema de arquivos de raiz virtual com controle sobre propriedade, permissões e assim por diante. Entretanto, as permissões e o material relacionado são mantidos em um banco de dados separado e nenhuma operação privilegiada real é necessária. Isto possibilita que um usuário não-root manipule hierarquias de diretório com privilégios de administrador virtual e enfim, crie arquivos ou imagens do sistema de arquivos contendo arquivos que o usuário não tinha os privilégios para criar. Para este tutorial, entretanto, eu assumo privilégios de administrador.

Alterando a raiz

O comando chroot embora certamente faça parte da história, não é suficiente para alterar o sistema de arquivos de raiz. A ferramenta para isto é pivot_root, que torna um diretório nomeado o novo diretório-raiz e "move" (realmente, altera o ponto de montagem) o antigo diretório-raiz antigo para outro nome.

Uma raiz não tão inicial

Para os propósitos deste tutorial, estou assumindo que a imagem initrd padrão fornecida com a placa é adequada. As distribuições do kernel Linux possuem algum suporte para geração de imagens initrd adequadas e os detalhes deste não são cruciais para um entendimento da criação de uma distribuição real. Eu simplesmente vou apresentar os principais itens para movimentar o sistema, então, focarei no que acontece no sistema de arquivos de raiz real.

Imagens do sistema de arquivos

A maioria dos usuários Linux estão familiarizados com arquivos contendo imagens dos sistemas de arquivos do CD-ROM ou DVD ISO. O Linux suporta a criação e a utilização das imagens de outros tipos de sistemas de arquivos. De fato, qualquer arquivo pode ser tratado como se estivesse em uma partição de disco, utilizando a opção -o loop para mount. Por exemplo, durante a pesquisa, eu fiz uma cópia da imagem initrd utilizada pelo TS-7800:

$ dd if=/dev/sdd3 of=initrd.dist bs=16k
$ cp initrd.dist initrd.local

Com este arquivo disponível, é possível montar o sistema de arquivos para visualizá-lo. A cópia denominada initrd.local é a versão local para editar; a cópia denominada initrd.dist é uma cópia primitiva segura no caso de algo se quebrar posteriormente.

$ mount -o loop initrd.local /mnt

O que o initrd faz?

O initrd fornece um sistema de arquivos inicial bastante básico utilizado para autoinicialização do dispositivo localizando e montando o sistema de arquivos de raiz real. A configuração padrão de kernel é conectada para utilizar um initrd (/dev/ram0) como root e executa o script linuxrc na inicialização. Há diversas variantes fornecidas deste script para diferentes sistemas de arquivos de raiz diferentes. A opção óbvia é consultar a variante linuxrc-sdroo .

Preparando o initrd

O programa linuxrc-sdroot tenta configurar o sistema para utilizar uma placa SD como raiz. Assumindo uma placa SD corretamente configurada com um sistema de arquivos ext3 na quarta partição fdisk, o script monta tal sistema de arquivos e executa /sbin/init nele. Isto parece uma boa estratégia. Portanto, algumas pequenas alterações não necessárias na imagem do initrd. A primeira é alterar o link simbólico linuxrc para apontar para o linuxrc-sdroot. A segunda é atualizar o script para trabalhar com a construção uClibc ao invés da construção do Debian para a qual foi configurada.

O driver SD

O driver SD para o TS-7800 inclui um módulo proprietário, portanto, o arquivo tssdcard.ko simplesmente precisa ser manualmente copiado e carregado no tempo de execução. O arquivo no initrd da distribuição funciona bem com o novo kernel, portanto, nenhuma alteração é necessária.

Alternando as rotinas de autoinicialização

O kernel simplesmente executa o programa denominado linuxrc. Por padrão, este é um link simbólico para o programa linuxrc-fastboot, que chega rapidamente até o sistema de arquivos de raiz do initrd. Remover o link e criar um link linuxrc ao invés do linuxrc-sdroot, faz com que o sistema tente executar o boot a partir da última partição no SD uma vez que os drivers são carregados.

Alterando a rotina de autoinicialização

O script verifica se há erros e se localizar algum, monta a partir da atualização interna. Se não houver erros, ele monta o sistema de arquivos a partir da placa SD e, então, tenta alterá-lo. A rotina de autoinicialização realmente tem uma pequena peculiaridade que afeta esta construção; ela tenta utilizar seu próprio utilitário mount ao invés do rootfs, para o último trabalho de limpeza após chamar pivot_root. Isto funciona bem com a instalação Debian fornecida, mas falha com a instalação minimalista de uClibc. A solução é alterar as linhas que acompanham o comando pivot_root no final do arquivo (após a cláusula else) conforme a seguir:

pivot_root . ./initrd
/bin/mount -n --move ./initrd/dev ./dev
/bin/mount -n --move ./initrd/sys ./sys
/bin/mount -n --move ./initrd/proc ./proc
exec /sbin/init < $CONSOLE > $CONSOLE 2>&1

Isto faz com que o script execute os novos executáveis (uClibc, estaticamente vinculado) que você está prestes a criar, ao invés de tentar executar executáveis dinamicamente vinculados a partir de outro sistema. (Se você estava fazendo isto sem um tutorial, provavelmente não saberia disto até depois de ter configurado tudo e começado a receber mensagens de erro enigmáticas).

Desmonte a imagem do disco

Desmonte a imagem de disco initrd da seguinte maneira:

$ umount /mnt


Preenchendo o Sistema de Arquivos de Raiz

O que um sistema de arquivos de raiz contém?

O sistema de arquivos de raiz precisa de um /sbin/init que fará qualquer coisa que desejemos que ele faça. Este pode ser um programa customizado ao invés de um init convencional de estilo UNIX, mas é mais fácil trabalhar com o sistema se você fornecer um ambiente de trabalho completo com o shell do console e tudo mais.

Bibliotecas e cabeçalhos básicos

A construção crosstool-ng criou uma raiz do sistema padrão fornecendo código de biblioteca e cabeçalhos. Faça uma cópia dele:

$ cp -r toolchain/arm-unknown-linux-uclibc/sys-root rootfs

Observe que nenhum esforço está sendo feito para corrigir permissões no momento; para uma distribuição real, você provavelmente desejaria corrigir isto, mas isto é inofensivo o suficiente para propósitos de demonstração. Isto traz a você as primeiras partes básicas de um sistema de arquivos de raiz, contendo apenas as bibliotecas (e cabeçalhos) que as construções utilizarão.

Busybox

No que se refere aos sistemas de arquivos de raiz integrados, o busybox é, provavelmente, o melhor ponto de partida. O Busybox oferece um conjunto completo razoável de utilitários do sistema, todos integrados em um único binário, que utiliza o nome com o qual é chamado para determinar qual função executar. Isto, junto a uma grande variedade de links, possibilita que um número indeterminado dos principais programas do sistema sejam incluídos em um pacote configurável na pequena quantidade de espaço. Isto nem sempre é necessário, mas é conveniente para os nossos propósitos.

Fazendo download do busybox

O Busybox pode ser transferido por download a partir de sua página de downloads (consulte Recursos). O archive é apenas um tarball padrão e pode ser descompactado em um diretório de construção. Eu utilize a versão 1.10.2.

Configurando o busybox

O Busybox é construído ao redor de ferramentas configuração semelhantes àquelas utilizadas para o crosstool-ng e o kernel Linux. Para construir uma configuração padrão, execute make defconfig. Isto cria a configuração padrão. Um make menuconfig seguinte permite que você altere configurações; sob "Configurações do Busybox," o menu "Construir Opções" permite que você especifique uma construção estática. Faça isso porque a construção padrão do crosstool-ng do uClibc o requer. Em alguns casos, você pode preferir um link dinamicamente vinculado, para reduzir o tamanho do executável, mas o problema do tamanho não é tão sério em um sistema no qual praticamente todos os binários são apenas um link simbólico. Assim como com o kernel, o Makefile de nível superior CROSS_COMPILE que contém o prefixo a utilizar nos nomes das ferramentas do compilador. Configure-o com o mesmo valor utilizado antes.

Construindo o busybox

Para construir o busybox, simplesmente execute make. Surpreendentemente, houve um problema de compilação durante a construção, que exigiu que eu incluísse <linux/types.h> no cabeçalho libbb.h principal. Com esse problema resolvido, a compilação foi concluída rapidamente.

Instalando o busybox

Para instalar o busybox, você precisa executar make install, mas especifique o caminho do sistema de arquivos:

$ make CONFIG_PREFIX=$HOME/7800/rootfs install

Isto copia no binário busybox e cria todos os links necessários. Este sistema de arquivos de raiz agora possui bibliotecas e todos os utilitários UNIX comuns. O que está faltando?

Nós de dispositivo

Você precisa de nós de dispositivo. Há duas maneiras de fornecer nós de dispositivo para um sistema de arquivos de raiz. Uma é criar todos os nós de dispositivo estaticamente que você planeja ter, a outra é utilizar algo semelhante a udev, que fornece uma árvore automática de nós de dispositivo atuais para o kernel atual. Embora udev seja extremamente elegante, ele também pode ser razoavelmente lento e os sistemas embarcados têm o luxo de serem capazes de predizer seus componentes de hardware.

Criando os nós de dispositivo necessários

Muitos programas dependem do acesso aos nós de dispositivo, tais como /dev/console ou /dev/null. Os nós de dispositivo normalmente são criados no tempo de execução nos sistemas ARM, utilizando o utilitário mdev, parte do busybox. De fato, isto já foi feito pelo sistema de arquivos initrd; uma das alterações acima foi mover o ponto de montagem /dev já preenchido no novo sistema de arquivos de raiz. Você pode criar nós de dispositivo chave manualmente, mas isto normalmente não vale a pena; o mdev é mais inteligente.

Pontos de montagem

Diversos pontos de montagem são necessários. O programa /sys e os pontos de montagem /proc são utilizados para mover os sistemas de arquivos virtuais sysfs e procfs. O diretório /dev é utilizado como um ponto de montagem para um sistema de arquivos tmpfs, preenchido pelo mdev. Estes diretórios não precisam de nenhum outro conteúdo:

$ cd $HOME/7800/rootfs
$ mkdir sys proc dev

Etcetera

O diretório /etc, embora não seja estritamente necessário para obter uma linha de comando do shell, é muito útil acessar uma linha de comando do shell. A parte mais importante durante a execução de boot inicial é o subdiretório init.d, que o init procura por arquivos de inicialização do sistema. Crie um script rcS, trivial e forneça a ele permissões de execução para a inicialização do sistema inicial.

$ mkdir rootfs/etc/init.d

Crie o arquivo rootfs/etc/init.d/rcS com os seguintes conteúdos:

#!/bin/sh
echo "Hello, world!"

Agora, marque-o como executável:

$ chmod 755 rootfs/etc/init.d/rcS


Criando uma Imagem do Disco

Faça um arquivo vazio grande

Você pode criar uma imagem do sistema de arquivos copiando blocos do /dev/zero em um arquivo ou apenas copiando uma imagem existente. Não importa o tamanho do espaço que você tenha disponível (lembrando que 12 MB já estão reservados para as primeiras três partições), é possível criar um arquivo com tal tamanho com o dd. Isto cria um sistema de arquivos de raiz de 64 MB bastante espaçoso:

$ dd if=/dev/zero of=rootfs.local bs=1M count=64

Formatar o arquivo

Os vários utilitários mkfs podem ser executados nos arquivos, não apenas nos discos. Para construir um sistema de arquivos de raiz ext3 (o tipo que o initrd procura), execute mkfs.ext3 em sua imagem do disco:

$ mkfs.ext3 rootfs.local

O utilitário mkfs pergunta se você vai continuar ou não, mesmo que o arquivo não seja um dispositivo de bloco especial:

rootfs.tmp não é um dispositivo de bloco especial.
Continuar mesmo assim?(s,n) s

Monte a nova imagem do disco

Para montar a imagem do disco, utilize a opção -o loop para o comando mount novamente para montar a imagem em algum lugar.

$ mount -o loop rootfs.local /mnt

Copie os arquivos rootfs no disco montado

O utilitário pax é particularmente bom nisto.

$ cd 7800/rootfs
$ pax -r -w -p e . /mnt

Isto copia a árvore do diretório que você acabou de criar em /mnt, preservando links simbólicos e arquivos especiais (exceto para soquetes, mas você pode viver o soquete do log).

Desmontar a imagem

Novamente, após ter concluído o trabalho na imagem, desmonte-a:

$ umount /mnt


Recuperando as Imagens na Placa

Copiando arquivos de volta

Você pode imaginar porque o procedimento acima simplesmente não utilizou a placa como um sistema de arquivos. A resposta é que, em geral, é melhor não utilizar sistemas de arquivos de atualização muito pesadamente, porque os dispositivos de atualização têm expectativas de vida muito mais curtas do que a mídia de unidade de disco rígido sob carga pesada. Portanto, as complicadas edições e cópias são feitas nas imagens do disco, então, as imagens do disco são copiadas de volta como uma única operação de gravação.

Copiar no initrd

Copie a imagem do initrd na terceira partição da placa SD:

$ dd if=initrd.local of=/dev/sdd3 bs=16k

Copiar no sistema de arquivos de raiz

Da mesma forma, copie a imagem do sistema de arquivos na quarta partição da placa SD.

$ dd if=rootfs.local of=/dev/sdd4 bs=16k

Aguardar para acesso ao dispositivo

Não retire a placa SD do leitor imediatamente. Algumas vezes, podem ser necessários alguns segundos extras para que o sistema conclua a gravação. Aguarde até que todas as luzes de atividade tenha se estabilizado e, então, aguarde alguns segundos para ter certeza.


O Processo de Boot Revisado

O que o init faz?

O programa init gerencia a inicialização do sistema, e, então, mantém o sistema em execução. Por exemplo, quando o processo acaba, o init é o programa que coleta seus valores de status de saída para que o kernel possa concluir o carregamento deles. O procedimento de inicialização exato varia de uma versão do Linux para outra. Neste caso, é a versão busybox do init que executará o boot do nosso sistema de teste.

Você pode ter imaginado porque a versão anterior do script de inicialização utiliza exec /sbin/init ao invés de apenas chamar o init. O motivo é que o script de inicialização era o init anterior e tinha o ID de processo 1. O programa init não se torna o daemon de inicialização do sistema a menos que seu ID de processo seja 1; exec faz com que ele assuma o comando sobre o ID do processo do shell de chamada, ao invés de adquirir um novo.

Inicialização inicial

O programa init é iniciado executando o primeiro script de inicialização do sistema, /etc/init.d/rcS. (Se não houver tal arquivo, ele imprime uma mensagem de aviso e continua sem ele). Depois disso, ele executa de acordo com as instruções em /etc/inittab, se existir. Na ausência de um /etc/inittab, o init do busybox executa um shell no console e lida bem com a reinicialização e pedidos de parada.

Reunindo

Com seu próprio kernel, um initrd levemente customizado e seu próprio sistema de arquivos de raiz, você agora deve ser capaz de executar boot no sistema para uma linha de comando do shell. Se você criou o arquivo rcS, o sistema deve saudá-lo no boot. Agora, você tem um sistema Linux em funcionamento e, certamente, mínimo, construído inteiramente a partir da origem e montado à mão.

O que você vai fazer a seguir é com você. Eu sugiro instalar alguns videogames, mas sempre poderá configurar o sistema para um servidor de banco de dados ou servidor da Web. Se você planejar fazer algo que produzirá muita atividade no disco, considere utilizar uma unidade de disco rígida conectada via USB.

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux
ArticleID=386614
ArticleTitle=Criar uma distribuição Linux integrada a partir do zero
publish-date=08122008