Quais são os aplicativos mínimos do sistema de arquivos raiz necessários para inicializar completamente o Linux?

16

É uma pergunta sobre aplicativos de espaço do usuário, mas ouça!

Três "aplicativos", por assim dizer, são necessários para inicializar uma distribuição funcional do Linux:

  1. Carregador de Inicialização - Para embutido, tipicamente isso é U-Boot, embora não seja um requisito difícil.

  2. Kernel - Isso é bem direto.

  3. Sistema de arquivos raiz - Não é possível inicializar em um shell sem ele. Contém o sistema de arquivos no qual o kernel inicializa e onde inité chamado de formulário.

Minha pergunta é em relação ao item 3. Se alguém quisesse criar um rootfs extremamente mínimo (para esta pergunta, digamos que não haja GUI, apenas shell), quais arquivos / programas são necessários para inicializar em um shell?

MDMoore313
fonte
Defina o mínimo. Você pode usar apenas um único executável com nada mais, conforme explicado em: superuser.com/a/991733/128124 Só que ele não pode sair nem entrar em pânico; portanto, você precisa de um loop infinito ou de um longo sono. Similar pergunta: unix.stackexchange.com/questions/17122/...
Ciro Santilli新疆改造中心法轮功六四事件

Respostas:

31

Isso depende inteiramente de quais serviços você deseja ter no seu dispositivo.

Programas

Você pode fazer o Linux inicializar diretamente em um shell . Não é muito útil na produção - quem apenas gostaria de ter um shell - mas é útil como mecanismo de intervenção quando você tem um gerenciador de inicialização interativo: passe init=/bin/shpara a linha de comando do kernel. Todos os sistemas Linux (e todos os sistemas unix) possuem um shell no estilo Bourne / POSIX /bin/sh.

Você precisará de um conjunto de utilitários de shell . BusyBox é uma escolha muito comum; ele contém um shell e utilitários comuns de arquivo e manipulação de texto ( cp, grep...), configuração de rede ( ping, ifconfig...), manipulação de processo ( ps, nice...), e várias outras ferramentas de sistema ( fdisk, mount, syslogd, ...). O BusyBox é extremamente configurável: você pode selecionar quais ferramentas deseja e até recursos individuais em tempo de compilação, para obter o tamanho / funcionalidade certos para o seu aplicativo. Além de sh, o mínimo que você realmente não pode fazer nada sem é mount, umounte halt, mas seria atípico para não ter também cat, cp, mv, rm,mkdir, rmdir, ps, syncE um pouco mais. O BusyBox é instalado como um único binário chamado busybox, com um link simbólico para cada utilitário.

O primeiro processo em um sistema unix normal é chamado init. Seu trabalho é iniciar outros serviços. BusyBox contém um sistema init. Além do initbinário (geralmente localizado em /sbin), você precisará de seus arquivos de configuração (geralmente chamados /etc/inittab- algumas substituições init modernas eliminam esse arquivo, mas você não os encontrará em um pequeno sistema embutido) que indicam quais serviços iniciar e quando. Para o BusyBox, /etc/inittabé opcional; se estiver ausente, você obtém um shell raiz no console e o script /etc/init.d/rcS(local padrão) é executado no momento da inicialização.

É tudo o que você precisa, além dos programas que tornam seu dispositivo útil. Por exemplo, no meu roteador doméstico executando uma variante OpenWrt , os únicos programas são o BusyBox nvram(para ler e alterar as configurações na NVRAM) e os utilitários de rede.

A menos que todos os seus executáveis ​​estejam vinculados estaticamente, você precisará do carregador dinâmico ( ld.soque pode ser chamado por nomes diferentes, dependendo da escolha da libc e das arquiteturas do processador) e de todas as bibliotecas dinâmicas ( /lib/lib*.sotalvez algumas delas /usr/lib) exigidas por esses executáveis.

Estrutura de diretórios

O padrão de hierarquia de sistemas de arquivos descreve a estrutura de diretórios comum dos sistemas Linux. Ele é voltado para instalações de desktop e servidor: muitas delas podem ser omitidas em um sistema incorporado. Aqui está um mínimo típico.

  • /bin: programas executáveis ​​(alguns podem estar no /usr/binlugar).
  • /dev: nós do dispositivo (veja abaixo)
  • /etc: arquivos de configuração
  • /lib: bibliotecas compartilhadas, incluindo o carregador dinâmico (a menos que todos os executáveis ​​estejam vinculados estaticamente)
  • /proc: ponto de montagem para o sistema de arquivos proc
  • /sbin: programas executáveis. A diferença /biné que /sbiné para programas que são úteis apenas ao administrador do sistema, mas essa distinção não é significativa em dispositivos incorporados. Você pode criar /sbinum link simbólico para /bin.
  • /mnt: prático para ter em sistemas de arquivos raiz somente leitura como um ponto de montagem inicial durante a manutenção
  • /sys: ponto de montagem para o sistema de arquivos sysfs
  • /tmp: local para arquivos temporários (geralmente uma tmpfsmontagem)
  • /usr: Contém subdiretórios bin, libe sbin. /usrexiste para arquivos extras que não estão no sistema de arquivos raiz. Se você não tiver isso, poderá criar /usrum link simbólico para o diretório raiz.

Arquivos de dispositivo

Aqui estão algumas entradas típicas em um mínimo /dev:

  • console
  • full (escrever nele sempre informa "não resta espaço no dispositivo")
  • log(um soquete que os programas usam para enviar entradas de log), se você tiver um syslogddaemon (como o do BusyBox) lendo
  • null (age como um arquivo sempre vazio)
  • ptmxe um ptsdiretório , se você quiser usar pseudo-terminais (ou seja, qualquer outro terminal que não seja o console) - por exemplo, se o dispositivo estiver em rede e você desejar telnet ou ssh
  • random (retorna bytes aleatórios, corre o risco de bloquear)
  • tty (sempre designa o terminal do programa)
  • urandom (retorna bytes aleatórios, nunca bloqueia, mas pode ser não aleatório em um dispositivo recém-inicializado)
  • zero (contém uma sequência infinita de bytes nulos)

Além disso, você precisará de entradas para o seu hardware (exceto interfaces de rede, elas não recebem entradas /dev): portas seriais, armazenamento etc.

Para dispositivos incorporados, você normalmente criaria as entradas do dispositivo diretamente no sistema de arquivos raiz. Os sistemas high-end têm um script chamado MAKEDEVpara criar /deventradas, mas em um sistema incorporado o script geralmente não é empacotado na imagem. Se algum hardware puder ser conectado a quente (por exemplo, se o dispositivo tiver uma porta host USB), ele /devdeverá ser gerenciado pelo udev (você ainda pode ter um conjunto mínimo no sistema de arquivos raiz).

Ações de inicialização

Além do sistema de arquivos raiz, você precisa montar um pouco mais para a operação normal:

  • procfs on /proc(praticamente indispensável)
  • sysfs on /sys(praticamente indispensável)
  • tmpfssistema de arquivos ativado /tmp(para permitir que os programas criem arquivos temporários que estarão na RAM, em vez de no sistema de arquivos raiz que pode estar em flash ou somente leitura)
  • tmpfs, devfs ou devtmpfs on /devse dinâmico (consulte udev em “Arquivos de dispositivo” acima)
  • devpts sobre /dev/ptsse você quiser usar pseudo-terminais [(ver a observação sobre ptsacima)

Você pode criar um /etc/fstabarquivo e ligar mount -aou executar mountmanualmente.

Inicie um daemon syslog (assim como klogdpara logs do kernel, se o syslogdprograma não cuidar disso), se você tiver algum lugar para gravar os logs.

Depois disso, o dispositivo está pronto para iniciar serviços específicos do aplicativo.

Como criar um sistema de arquivos raiz

Esta é uma história longa e diversificada, então tudo o que farei aqui é dar algumas dicas.

O sistema de arquivos raiz pode ser mantido na RAM (carregada de uma imagem (geralmente compactada) em ROM ou flash) ou em um sistema de arquivos baseado em disco (armazenado em ROM ou flash) ou carregado na rede (geralmente através de TFTP ), se aplicável . Se o sistema de arquivos raiz estiver na RAM, torne-o initramfs - um sistema de arquivos RAM cujo conteúdo é criado no momento da inicialização.

Existem muitas estruturas para montar imagens raiz para sistemas incorporados. Existem algumas dicas na FAQ do BusyBox . O Buildroot é popular, permitindo criar uma imagem raiz inteira com uma configuração semelhante ao kernel do Linux e ao BusyBox. OpenEmbedded é outra dessas estruturas.

A Wikipedia possui uma lista (incompleta) de distribuições Linux embarcadas populares . Um exemplo de Linux incorporado que você pode ter perto de você é a família de sistemas operacionais OpenWrt para dispositivos de rede (popular nos roteadores domésticos dos consertadores). Se você quer aprender por experiência, pode experimentar o Linux a partir do Scratch , mas é voltado para sistemas de desktop para entusiastas do que para dispositivos embarcados.

Uma observação sobre o kernel Linux vs Linux

O único comportamento inserido no kernel do Linux é o primeiro programa lançado no momento da inicialização. (Não abordarei as sutilezas do initrd e initramfs aqui.) Este programa, tradicionalmente chamado de init , possui o ID do processo 1 e possui certos privilégios (imunidade a sinais KILL ) e responsabilidades (colher órfãos ). Você pode executar um sistema com um kernel Linux e iniciar o que quiser como primeiro processo, mas o que você tem é um sistema operacional baseado no kernel Linux, e não o que normalmente é chamado de "Linux" -  Linux , no senso comum do termo, é um sistema operacional semelhante ao Unix cujo kernel é o kernel Linux. Por exemplo, o Android é um sistema operacional que não é semelhante ao Unix, mas baseado no kernel do Linux.

Gilles 'SO- parar de ser mau'
fonte
Excelente resposta. Eu mencionei apenas a inicialização no Linux no título b / c, que é o que provavelmente será pesquisado, tão grande adição sobre o Linux vs Linux Kernel, que precisa ser um conhecimento mais amplo.
MDMoore313
@BigHomie Lembre-se, a Free Software Foundation quer que todos chamemos de GNU / Linux, já que na maioria das "distros Linux" o software é GNU, mesmo que o kernel seja Linux (daí o GNU / Linux).
BenjiWiebe
Meh, ninguém tem tempo para isso. Então minha distro deve se chamar Busybox / Linux ?? Eu sei que eu sei, não é você seu Stallworth, apenas desabafando;)
MDMoore313
11
@BenjiWiebe ou GNU / X11 / Apache / Linux / TeX / Perl / Python / FreeCiv . Além do RMS, todo mundo chama de "Linux".
Gilles 'SO- stop be evil'
@ Gilles Bem, além do Debian, eu acho. :)
um CVn
5

Tudo o que você precisa é de um executável vinculado estaticamente, colocado no sistema de arquivos, isoladamente. Você não precisa de outros arquivos. Esse executável é o processo de inicialização. Pode ser ocupado. Isso fornece um shell e uma série de outros utilitários, por si só. Você pode acessar um sistema totalmente funcional apenas executando comandos manualmente no busybox para montar a leitura / gravação do sistema de arquivos raiz, criar nós / dev, exec real init etc.

Restabelecer Monica
fonte
Sim, eu sabia que o busybox estava chegando. Vamos ver se algo mais aparece.
MDMoore313
4

Se você não precisar de nenhum utilitário de shell, um mkshbinário vinculado estaticamente (por exemplo, contra o klibc - 130K no Linux / i386) será necessário. Você precisa de um /linuxrcou /initou /sbin/initscript que apenas chame mksh -l -T!/dev/tty1em um loop:

#!/bin/mksh
while true; do
    /bin/mksh -l -T!/dev/tty1
done

A -T!$ttyopção é uma adição recente ao mkshque diz para gerar um novo shell no terminal fornecido e aguardar por ele. (Antes disso, havia apenas -T-a dæmonise um programm e -T$ttypara desovar em um terminal, mas não espere por isso. Isso não era tão bom.) A -lopção simplesmente diz-lhe para executar um shell de login (que lê /etc/profile, ~/.profilee ~/.mkshrc).

Isso pressupõe que seu terminal seja /dev/tty1substituto. (Com mais magia, o terminal pode ser descoberto automaticamente. /dev/consoleNão oferece controle total do trabalho.)

Você precisa de alguns arquivos /devpara que isso funcione:

  • / dev / console
  • / dev / null
  • / dev / tty
  • / dev / tty1

A inicialização com a opção kernel devtmpfs.mount=1elimina a necessidade de um preenchimento /dev, apenas seja um diretório vazio (adequado para uso como ponto de montagem).

Você normalmente deseja ter alguns utilitários (do klibc, busybox, beastiebox, toybox ou toolbox), mas eles não são realmente necessários.

Você pode querer adicionar um ~/.mkshrcarquivo, que configure $ PS1 e alguns aliases e funções básicos do shell.

Certa vez, criei um initrd compactado de 171K (371K não compactado) para Linux / m68k usando mksh (e seu arquivo mkshrc de amostra) e apenas o klibc-utils. (Isso foi antes de -T! Ser adicionado ao shell, no entanto, ele gerou o shell de login /dev/tty2e ecoou uma mensagem no console informando ao usuário para alternar terminais.) Funciona bem.

Esta é uma configuração mínima realmente simples . As outras respostas fornecem conselhos excelentes para sistemas um pouco mais destacados. Isso é realmente um caso especial.

Disclaimer: Eu sou o desenvolvedor mksh.

mirabilos
fonte
Esta é uma ótima resposta, obrigado por compartilhar e também por mksh.
precisa saber é o seguinte
2

Programa mínimo init hello world passo a passo

insira a descrição da imagem aqui

Compile um olá mundo sem dependências que terminem em um loop infinito. init.S:

.global _start
_start:
    mov $1, %rax
    mov $1, %rdi
    mov $message, %rsi
    mov $message_len, %rdx
    syscall
    jmp .
    message: .ascii "FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n"
    .equ message_len, . - message

Não podemos usar sys_exit, senão o kernel entra em pânico.

Então:

mkdir d
as --64 -o init.o init.S
ld -o init d/init.o
cd d
find . | cpio -o -H newc | gzip > ../rootfs.cpio.gz
ROOTFS_PATH="$(pwd)/../rootfs.cpio.gz"

Isso cria um sistema de arquivos com o nosso hello world at /init, que é o primeiro programa da terra do usuário em que o kernel será executado. Também poderíamos ter adicionado mais arquivos d/e eles seriam acessíveis a partir do /initprograma quando o kernel for executado.

Em seguida, cdna árvore do kernel do Linux, build é como de costume e execute-o no QEMU:

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
git checkout v4.9
make mrproper
make defconfig
make -j"$(nproc)"
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd "$ROOTFS_PATH"

E você deve ver uma linha:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR

na tela do emulador! Note que não é a última linha, então você precisa olhar um pouco mais adiante.

Você também pode usar programas C se os vincular estaticamente:

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n");
    sleep(0xFFFFFFFF);
    return 0;
}

com:

gcc -static init.c -o init

Você pode executar em hardware real com um USB ligado /dev/sdX e:

make isoimage FDINITRD="$ROOTFS_PATH"
sudo dd if=arch/x86/boot/image.iso of=/dev/sdX

Ótima fonte sobre este assunto: http://landley.net/writing/rootfs-howto.html Também explica como usar gen_initramfs_list.sh, que é um script da árvore de fontes do kernel Linux para ajudar a automatizar o processo.

Próxima etapa: configure o BusyBox para que você possa interagir com o sistema: https://github.com/cirosantilli/runlinux

Testado no Ubuntu 16.10, QEMU 2.6.1.

Ciro Santilli adicionou uma nova foto
fonte