Quali sono le applicazioni minime del filesystem di root necessarie per avviare completamente Linux?


17

È una domanda sulle applicazioni dello spazio utente, ma ascoltami!

Tre "applicazioni", per così dire, sono necessarie per avviare una distribuzione funzionale di Linux:

  1. Bootloader - In genere per U-Boot è U-Boot, sebbene non sia un requisito difficile.

  2. Kernel - È abbastanza semplice.

  3. Filesystem di root - Impossibile avviare una shell senza di essa. Contiene il filesystem su cui si avvia il kernel e dove initviene chiamato form.

La mia domanda riguarda il n. 3. Se qualcuno volesse creare un rootfs estremamente minimale (per questa domanda diciamo no GUI, solo shell), quali file / programmi sono necessari per avviare una shell?


Definire il minimo. Puoi usare solo un singolo eseguibile con nient'altro, come spiegato in: superuser.com/a/991733/128124 Solo che non può uscire da tutte le uscite o andare nel panico, quindi hai bisogno di un loop infinito o di un lungo sonno. Domanda simile: unix.stackexchange.com/questions/17122/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Risposte:


32

Dipende interamente dai servizi che desideri avere sul tuo dispositivo.

programmi

Puoi fare il boot di Linux direttamente in una shell . Non è molto utile in produzione - chi vorrebbe solo avere una shell seduta lì - ma è utile come meccanismo di intervento quando si dispone di un bootloader interattivo: passare init=/bin/shalla riga di comando del kernel. Tutti i sistemi Linux (e tutti i sistemi unix) hanno una shell in stile Bourne / POSIX /bin/sh.

Avrai bisogno di una serie di utility di shell . BusyBox è una scelta molto comune; contiene una shell e utility comuni per il file e la manipolazione di testo ( cp, grep, ...), la configurazione della rete ( ping, ifconfig, ...), la manipolazione di processo ( ps, nice, ...), e vari altri strumenti di sistema ( fdisk, mount, syslogd, ...). BusyBox è estremamente configurabile: è possibile selezionare gli strumenti desiderati e persino le singole funzionalità al momento della compilazione, per ottenere il giusto compromesso dimensioni / funzionalità per la propria applicazione. A parte sh, il minimo che non si può davvero fare nulla senza è mount, umounte halt, ma sarebbe atipico per non avere anche cat, cp, mv, rm,mkdir, rmdir, ps, syncE pochi altri. BusyBox si installa come un singolo binario chiamato busybox, con un collegamento simbolico per ogni utility.

Viene chiamato il primo processo su un normale sistema unix init. Il suo compito è avviare altri servizi. BusyBox contiene un sistema init. Oltre al initbinario (di solito si trova in /sbin), avrai bisogno dei suoi file di configurazione (di solito chiamati /etc/inittab- alcuni moderni sostituti di init eliminano quel file ma non li troverai su un piccolo sistema incorporato) che indicano quali servizi avviare e quando. Per BusyBox, /etc/inittabè facoltativo; se manca, si ottiene una shell di root sulla console e lo script /etc/init.d/rcS(posizione predefinita) viene eseguito all'avvio.

Questo è tutto ciò di cui hai bisogno, oltre ovviamente ai programmi che rendono il tuo dispositivo utile. Ad esempio, sul mio router di casa che esegue una variante OpenWrt , gli unici programmi sono BusyBox, nvram(per leggere e modificare le impostazioni in NVRAM) e utilità di rete.

A meno che tutti i tuoi eseguibili non siano staticamente collegati, avrai bisogno del caricatore dinamico ( ld.so, che può essere chiamato con nomi diversi a seconda della scelta di libc e delle architetture del processore) e di tutte le librerie dinamiche ( /lib/lib*.so, forse alcune di queste /usr/lib) richieste da questi eseguibili.

Struttura di directory

Lo standard di gerarchia dei filesystem descrive la struttura di directory comune dei sistemi Linux. È orientato verso installazioni desktop e server: molte di esse possono essere omesse su un sistema incorporato. Ecco un minimo tipico.

  • /bin: programmi eseguibili (alcuni invece potrebbero essere presenti /usr/bin).
  • /dev: nodi dispositivo (vedi sotto)
  • /etc: file di configurazione
  • /lib: librerie condivise, incluso il caricatore dinamico (a meno che tutti gli eseguibili non siano staticamente collegati)
  • /proc: mount point per il filesystem proc
  • /sbin: programmi eseguibili. La distinzione /binè che si /sbintratta di programmi che sono utili solo all'amministratore di sistema, ma questa distinzione non è significativa sui dispositivi incorporati. È possibile creare /sbinun collegamento simbolico a /bin.
  • /mnt: utile avere nei filesystem di root di sola lettura come punto di montaggio scratch durante la manutenzione
  • /sys: mount point per il filesystem sysfs
  • /tmp: posizione per i file temporanei (spesso un tmpfsmount)
  • /usr: Contiene le sottodirectory bin, libe sbin. /usresiste per file extra che non si trovano nel filesystem di root. Se non lo possiedi, puoi creare /usrun collegamento simbolico alla directory principale.

File del dispositivo

Ecco alcune voci tipiche in un minimo /dev:

  • console
  • full (scrivendoci riporta sempre "nessuno spazio lasciato sul dispositivo")
  • log(un socket che i programmi usano per inviare voci di registro), se hai un syslogddemone (come quello di BusyBox) che legge da esso
  • null (si comporta come un file sempre vuoto)
  • ptmxe una ptsdirectory , se si desidera utilizzare pseudo-terminali (ovvero qualsiasi terminale diverso dalla console), ad esempio se il dispositivo è collegato in rete e si desidera telnet o ssh in
  • random (restituisce byte casuali, blocco dei rischi)
  • tty (indica sempre il terminale del programma)
  • urandom (restituisce byte casuali, non si blocca mai ma potrebbe non essere casuale su un dispositivo appena avviato)
  • zero (contiene una sequenza infinita di byte null)

Oltre a ciò avrai bisogno di voci per il tuo hardware (tranne le interfacce di rete, queste non ottengono voci /dev): porte seriali, archiviazione, ecc.

Per i dispositivi incorporati, normalmente si creano le voci del dispositivo direttamente sul filesystem di root. I sistemi di fascia alta hanno uno script chiamato MAKEDEVper creare /devvoci, ma su un sistema incorporato lo script spesso non è raggruppato nell'immagine. Se è possibile collegare a caldo alcuni componenti hardware (ad es. Se il dispositivo ha una porta host USB), allora /devdovrebbe essere gestito da udev (è possibile che sia ancora presente un set minimo sul filesystem di root).

Azioni all'avvio

Oltre al filesystem di root, è necessario montarne alcuni in più per il normale funzionamento:

  • procfs on /proc(praticamente indispensabile)
  • sysfs on /sys(praticamente indispensabile)
  • tmpfsfilesystem on /tmp(per consentire ai programmi di creare file temporanei che saranno nella RAM, piuttosto che sul filesystem di root che potrebbe essere in flash o in sola lettura)
  • tmpfs, devfs o devtmpfs su /devse dinamico (vedere udev in "File dispositivo" sopra)
  • devpts su /dev/ptsse si desidera utilizzare [pseudo-terminali (si veda l'osservazione sulla ptssopra)

È possibile creare un /etc/fstabfile e chiamare mount -ao eseguirlo mountmanualmente.

Avvia un demone syslog (oltre che klogdper i log del kernel, se il syslogdprogramma non se ne occupa), se hai un posto dove scrivere i log.

Successivamente, il dispositivo è pronto per avviare servizi specifici dell'applicazione.

Come creare un filesystem di root

Questa è una storia lunga e diversificata, quindi tutto ciò che farò qui è dare alcuni suggerimenti.

Il filesystem di root può essere mantenuto nella RAM (caricato da un'immagine (di solito compressa) nella ROM o nella memoria flash), o su un filesystem basato su disco (memorizzato nella memoria ROM o nella memoria flash) o caricato dalla rete (spesso tramite TFTP ) se applicabile . Se il filesystem di root è nella RAM, impostalo come initramfs - un filesystem RAM il cui contenuto viene creato all'avvio.

Esistono molti framework per assemblare immagini di root per sistemi embedded. Ci sono alcuni suggerimenti nelle FAQ di BusyBox . Buildroot è popolare, che consente di creare un'immagine di root completa con un'installazione simile al kernel Linux e BusyBox. OpenEmbedded è un altro di questi framework.

Wikipedia ha un elenco (incompleto) delle più diffuse distribuzioni Linux integrate . Un esempio di Linux incorporato che potresti avere vicino a te è la famiglia di sistemi operativi OpenWrt per dispositivi di rete (popolare sui router domestici di tinkerer). Se vuoi imparare per esperienza, puoi provare Linux da Scratch , ma è orientato verso sistemi desktop per hobbisti piuttosto che verso dispositivi embedded.

Una nota sul kernel Linux vs Linux

L'unico comportamento inserito nel kernel di Linux è che il primo programma lanciato all'avvio. (Non entrerò nelle sottigliezze di initrd e initramfs qui.) Questo programma, tradizionalmente chiamato init , ha l'ID processo 1 e ha alcuni privilegi (immunità ai segnali KILL ) e responsabilità (raccolta di orfani ). Puoi eseguire un sistema con un kernel Linux e avviare quello che vuoi come primo processo, ma poi quello che hai è un sistema operativo basato sul kernel Linux e non quello che normalmente viene chiamato "Linux" -  Linux , nel senso comune del termine, è un sistema operativo simile a Unix il cui kernel è il kernel Linux. Ad esempio, Android è un sistema operativo che non è simile a Unix ma basato sul kernel Linux.


Risposta eccellente. Ho menzionato solo l'avvio in Linux nel titolo b / c che è quello che probabilmente verrà cercato, così grande aggiunta su Linux vs Linux Kernel, che deve essere una conoscenza più diffusa.
MDMoore313,

@BigHomie Ricorda, la Free Software Foundation vuole che tutti noi lo chiamiamo GNU / Linux, poiché sulla maggior parte (tutti?) "Linux distro" il software è GNU, anche se il kernel è Linux (quindi GNU / Linux).
BenjiWiebe,

Meh, nessuno ha tempo per quello. Quindi la mia distro dovrebbe essere chiamata Busybox / Linux ?? Lo so lo so, non sei tu Stallworth, stai solo sfogando;)
MDMoore313


@Gilles Beh, a parte Debian, immagino. :)
un CVn

5

Tutto ciò che serve è un eseguibile collegato staticamente, posizionato sul filesystem, in isolamento. Non hai bisogno di altri file. L'eseguibile è il processo init. Può essere busybox. Questo ti dà una shell e una miriade di altre utilità, tutto in sé. Puoi andare su un sistema perfettamente funzionante semplicemente eseguendo i comandi manualmente in busybox per montare il filesystem di root in lettura-scrittura, creare nodi / dev, exec vero init, ecc.


Sì, sapevo che stava arrivando busybox. Vediamo se si presenta qualcos'altro.
MDMoore313,

4

Se non hai bisogno di alcuna utilità di shell, funzionerà un mkshbinario collegato staticamente (es. Contro klibc - 130K su Linux / i386). Hai bisogno di una /linuxrco di /inito /sbin/initscript che solo le chiamate mksh -l -T!/dev/tty1in un ciclo:

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

L' -T!$ttyopzione è una recente aggiunta a mkshquella che dice di generare una nuova shell sul terminale dato e di attendere. (Prima di allora, c'era solo -T-ad dæmonise un programma e -T$ttyper deporre le uova su un terminale, ma non aspettare per questo. Questo non era così bello.) L' -lopzione dice semplicemente per eseguire una shell di login (che legge /etc/profile, ~/.profilee ~/.mkshrc).

Ciò presuppone che il tuo terminale sia /dev/tty1, sostituto. (Con più magia, il terminale può essere scoperto automaticamente. /dev/consoleNon ti darà il pieno controllo del lavoro.)

/devPer far funzionare tutto ciò sono necessari alcuni file :

  • / Dev / console
  • / Dev / null
  • / Dev / tty
  • / Dev / tty1

L'avvio con l'opzione kernel devtmpfs.mount=1elimina la necessità di un riempimento /dev, basta lasciare che sia una directory vuota (adatta per l'uso come mountpoint).

Normalmente vorrai avere alcune utilità (da klibc, busybox, beastiebox, toybox o toolbox), ma non sono davvero necessarie.

Potresti voler aggiungere un ~/.mkshrcfile, che configura $ PS1 e alcuni alias e funzioni di base della shell.

Una volta ho creato un initrd compresso (371K non compresso) 171K per Linux / m68k usando solo mksh (e il suo file di esempio mkshrc) e klibc-utils. (Questo prima che -T! Fosse aggiunto alla shell, tuttavia, quindi /dev/tty2generò invece la shell di login e fece eco alla console un messaggio che diceva all'utente di cambiare terminale.) Funziona bene.

Questa è una configurazione minima davvero nuda . Le altre risposte forniscono ottimi consigli su sistemi un po 'più caratterizzati. Questa è una cosa davvero speciale.

Disclaimer: sono lo sviluppatore di mksh.


Questa è un'ottima risposta, grazie per la condivisione e anche per mksh.
JoshuaRLi

2

Programma minimo ciao init ciao passo dopo passo

inserisci qui la descrizione dell'immagine

Compila un mondo ciao senza dipendenze che termina in un ciclo 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

Non possiamo usare sys_exit , altrimenti i panici del kernel.

Poi:

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"

Questo crea un filesystem con il nostro ciao mondo su /init, che è il primo programma per l'utente che verrà eseguito dal kernel. Avremmo anche potuto aggiungere più file ad/ e sarebbero accessibili dal /initprogramma quando il kernel è in esecuzione.

Quindi, cdnell'albero del kernel Linux, build è come al solito ed eseguirlo in 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 dovresti vedere una linea:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR

sullo schermo dell'emulatore! Nota che non è l'ultima riga, quindi devi cercare un po 'più in alto.

Puoi anche usare i programmi C se li colleghi staticamente:

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

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

con:

gcc -static init.c -o init

È possibile eseguire su hardware reale con un USB acceso /dev/sdXe:

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

Grande fonte su questo argomento: http://landley.net/writing/rootfs-howto.html Spiega anche come usare gen_initramfs_list.sh, che è uno script dall'albero dei sorgenti del kernel Linux per automatizzare il processo.

Passaggio successivo: imposta BusyBox in modo da poter interagire con il sistema: https://github.com/cirosantilli/runlinux

Testato su Ubuntu 16.10, QEMU 2.6.1.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.