Quelles sont les applications minimales du système de fichiers racine requises pour démarrer complètement Linux?

17

C'est une question sur les applications de l'espace utilisateur, mais écoutez-moi!

Pour ainsi dire, trois "applications" sont nécessaires pour démarrer une distribution fonctionnelle de Linux:

  1. Bootloader - Pour les applications intégrées, il s'agit généralement de U-Boot, mais ce n'est pas une exigence difficile.

  2. Noyau - C'est assez simple.

  3. Système de fichiers racine - ne peut pas démarrer sans un shell. Contient le système de fichiers vers lequel le noyau démarre et où initest appelé formulaire.

Ma question concerne le numéro 3. Si quelqu'un voulait construire un rootfs extrêmement minimal (pour cette question, disons pas d'interface graphique, shell uniquement), quels fichiers / programmes sont nécessaires pour démarrer sur un shell?

MDMoore313
la source
Définissez minimal. Vous pouvez utiliser un seul exécutable avec rien d'autre comme expliqué sur: superuser.com/a/991733/128124 Juste qu'il ne peut pas sortir à chaque sortie ou paniquer, vous avez donc besoin d'une boucle infinie ou d'une longue veille. Question similaire: unix.stackexchange.com/questions/17122/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Réponses:

32

Cela dépend entièrement des services que vous souhaitez avoir sur votre appareil.

Programmes

Vous pouvez faire démarrer Linux directement dans un shell . Ce n'est pas très utile en production - qui voudrait juste avoir un shell assis là - mais c'est utile comme mécanisme d'intervention lorsque vous avez un chargeur de démarrage interactif: passez init=/bin/shà la ligne de commande du noyau. Tous les systèmes Linux (et tous les systèmes Unix) ont un shell de style Bourne / POSIX /bin/sh.

Vous aurez besoin d'un ensemble d' utilitaires shell . BusyBox est un choix très courant; il contient un shell et des utilitaires communs pour la manipulation de fichiers et le texte ( cp, grep, ...), la configuration du réseau ( ping, ifconfig, ...), la manipulation des processus ( ps, nice, ...), et divers autres outils système ( fdisk, mount, syslogd, ...). BusyBox est extrêmement configurable: vous pouvez sélectionner les outils que vous souhaitez et même les fonctionnalités individuelles au moment de la compilation, pour obtenir le bon compromis taille / fonctionnalité pour votre application. Mis à part sh, le strict minimum que vous ne pouvez pas vraiment faire quoi que ce soit sans est mount, umountet halt, mais ce serait atypique de ne pas avoir aussi cat, cp, mv, rm,mkdir, rmdir, ps, syncEt quelques autres. BusyBox s'installe comme un seul binaire appelé busybox, avec un lien symbolique pour chaque utilitaire.

Le premier processus sur un système Unix normal est appelé init. Son travail consiste à démarrer d'autres services. BusyBox contient un système d'initialisation. En plus du initbinaire (généralement situé dans /sbin), vous aurez besoin de ses fichiers de configuration (généralement appelés /etc/inittab- certains remplacements d'init modernes suppriment ce fichier mais vous ne les trouverez pas sur un petit système intégré) qui indiquent les services à démarrer et quand. Pour BusyBox, /etc/inittabest facultatif; s'il est manquant, vous obtenez un shell racine sur la console et le script /etc/init.d/rcS(emplacement par défaut) est exécuté au démarrage.

C'est tout ce dont vous avez besoin, au-delà bien sûr des programmes qui font que votre appareil fait quelque chose d'utile. Par exemple, sur mon routeur domestique exécutant une variante OpenWrt , les seuls programmes sont BusyBox nvram(pour lire et modifier les paramètres dans NVRAM) et les utilitaires de mise en réseau.

À moins que tous vos exécutables ne soient liés statiquement, vous aurez besoin du chargeur dynamique ( ld.so, qui peut être appelé par des noms différents selon le choix de libc et des architectures de processeur) et de toutes les bibliothèques dynamiques ( /lib/lib*.so, peut-être certaines d'entre elles /usr/lib) requises par ces exécutables.

Structure du répertoire

La norme de hiérarchie du système de fichiers décrit la structure de répertoires commune des systèmes Linux. Il est orienté vers les installations de postes de travail et de serveurs: une grande partie peut être omise sur un système embarqué. Voici un minimum typique.

  • /bin: programmes exécutables (certains peuvent être à la /usr/binplace).
  • /dev: nœuds de périphérique (voir ci-dessous)
  • /etc: fichiers de configuration
  • /lib: bibliothèques partagées, y compris le chargeur dynamique (sauf si tous les exécutables sont liés statiquement)
  • /proc: point de montage pour le système de fichiers proc
  • /sbin: programmes exécutables. La distinction avec /binest /sbinpour les programmes qui ne sont utiles qu'à l'administrateur système, mais cette distinction n'a pas de sens sur les périphériques intégrés. Vous pouvez créer /sbinun lien symbolique vers /bin.
  • /mnt: pratique à avoir sur les systèmes de fichiers racine en lecture seule comme point de montage scratch pendant la maintenance
  • /sys: point de montage pour le système de fichiers sysfs
  • /tmp: emplacement pour les fichiers temporaires (souvent un tmpfsmontage)
  • /usr: contient des sous bin- répertoires libet sbin. /usrexiste pour les fichiers supplémentaires qui ne sont pas sur le système de fichiers racine. Si vous n'en avez pas, vous pouvez créer /usrun lien symbolique vers le répertoire racine.

Fichiers de périphérique

Voici quelques entrées typiques dans un minimum /dev:

  • console
  • full (y écrire signale toujours «plus d'espace sur l'appareil»)
  • log(un socket que les programmes utilisent pour envoyer des entrées de journal), si vous avez un syslogddémon (tel que BusyBox) qui le lit
  • null (agit comme un fichier toujours vide)
  • ptmxet un ptsrépertoire , si vous souhaitez utiliser des pseudo-terminaux (c'est -à- dire tout terminal autre que la console) - par exemple, si le périphérique est en réseau et que vous souhaitez telnet ou ssh dans
  • random (retourne des octets aléatoires, risque de blocage)
  • tty (désigne toujours le terminal du programme)
  • urandom (retourne des octets aléatoires, ne bloque jamais mais peut être non aléatoire sur un appareil fraîchement démarré)
  • zero (contient une séquence infinie d'octets nuls)

Au-delà, vous aurez besoin d'entrées pour votre matériel (à l'exception des interfaces réseau, celles-ci ne sont pas entrées /dev): ports série, stockage, etc.

Pour les périphériques intégrés, vous devez normalement créer les entrées de périphérique directement sur le système de fichiers racine. Les systèmes haut de gamme ont un script appelé MAKEDEVpour créer des /deventrées, mais sur un système intégré, le script n'est souvent pas intégré à l'image. Si certains matériels peuvent être branchés à chaud (par exemple si le périphérique a un port hôte USB), alors ils /devdevraient être gérés par udev (vous pouvez toujours avoir un ensemble minimal sur le système de fichiers racine).

Actions au démarrage

Au-delà du système de fichiers racine, vous devez en monter quelques autres pour un fonctionnement normal:

  • procfs on /proc(assez indispensable)
  • sysfs sur /sys(à peu près indispensable)
  • tmpfssystème de fichiers activé /tmp(pour permettre aux programmes de créer des fichiers temporaires qui seront en RAM, plutôt que sur le système de fichiers racine qui peut être en flash ou en lecture seule)
  • tmpfs, devfs ou devtmpfs sur /devsi dynamique (voir udev dans «Fichiers de périphérique» ci-dessus)
  • devpts sur /dev/ptssi vous voulez utiliser [pseudo-terminaux (voir la remarque au sujet ptsci - dessus)

Vous pouvez créer un /etc/fstabfichier et appeler mount -aou exécuter mountmanuellement.

Démarrez un démon syslog (ainsi que klogdpour les journaux du noyau, si le syslogdprogramme ne s'en occupe pas), si vous avez un endroit où écrire des journaux.

Après cela, l'appareil est prêt à démarrer des services spécifiques à l'application.

Comment créer un système de fichiers racine

C'est une histoire longue et diversifiée, donc tout ce que je vais faire ici est de donner quelques conseils.

Le système de fichiers racine peut être conservé dans la RAM (chargé à partir d'une image (généralement compressée) dans la ROM ou le flash), ou sur un système de fichiers sur disque (stocké dans la ROM ou le flash), ou chargé à partir du réseau (souvent via TFTP ) le cas échéant . Si le système de fichiers racine est en RAM, faites-en l' initramfs - un système de fichiers RAM dont le contenu est créé au démarrage.

Il existe de nombreux frameworks pour assembler des images racine pour les systèmes embarqués. Il y a quelques pointeurs dans la FAQ BusyBox . Buildroot est populaire, vous permettant de construire une image racine entière avec une configuration similaire au noyau Linux et BusyBox. OpenEmbedded est un autre tel cadre.

Wikipedia a une liste (incomplète) de distributions Linux embarquées populaires . Un exemple de Linux embarqué que vous pouvez avoir près de chez vous est la famille de systèmes d'exploitation OpenWrt pour les appliances réseau (populaire sur les routeurs domestiques des bricoleurs). Si vous voulez apprendre par expérience, vous pouvez essayer Linux from Scratch , mais il est orienté vers les systèmes de bureau pour les amateurs plutôt que vers les appareils intégrés.

Une note sur Linux vs noyau Linux

Le seul comportement intégré au noyau Linux est le premier programme lancé au démarrage. (Je n'entrerai pas dans les subtilités initrd et initramfs ici.) Ce programme, traditionnellement appelé init , a l'ID de processus 1 et a certains privilèges (immunité aux signaux KILL ) et responsabilités (récolter des orphelins ). Vous pouvez exécuter un système avec un noyau Linux et démarrer tout ce que vous voulez comme premier processus, mais ce que vous avez est un système d'exploitation basé sur le noyau Linux, et non pas ce qui est normalement appelé «Linux» -  Linux , dans le sens commun du terme, est un système d' exploitation de type Unix dont le noyau est le noyau Linux. Par exemple, Android est un système d'exploitation qui n'est pas de type Unix mais basé sur le noyau Linux.

Gilles 'SO- arrête d'être méchant'
la source
Excellente réponse. J'ai seulement mentionné le démarrage sous Linux dans le titre b / c, c'est ce qui sera probablement recherché, donc un excellent ajout sur Linux vs Linux Kernel, qui doit être une connaissance plus répandue.
MDMoore313
@BigHomie Rappelez-vous, la Free Software Foundation veut que nous l'appelions tous GNU / Linux, car sur la plupart (toutes?) Des "distributions Linux", le logiciel est GNU, même si le noyau est Linux (d'où GNU / Linux).
BenjiWiebe
Meh, personne n'a le temps pour ça. Alors ma distribution devrait s'appeler Busybox / Linux ?? Je sais que je sais, ce n'est pas toi son Stallworth, juste une ventilation;)
MDMoore313
1
@BenjiWiebe ou GNU / X11 / Apache / Linux / TeX / Perl / Python / FreeCiv . En dehors de RMS, tout le monde l'appelle «Linux».
Gilles 'SO- arrête d'être méchant'
@ Gilles Eh bien, à part Debian, je suppose. :)
un CVn le
5

Tout ce dont vous avez besoin est un exécutable lié statiquement, placé sur le système de fichiers, de manière isolée. Vous n'avez pas besoin d'autres fichiers. Cet exécutable est le processus init. Il peut s'agir de busybox. Cela vous donne un shell et une foule d'autres utilitaires, en soi. Vous pouvez accéder à un système pleinement fonctionnel simplement en exécutant manuellement des commandes dans la boîte occupée pour monter le système de fichiers racine en lecture-écriture, créer des nœuds / dev, exécuter real init, etc.

Réintégrer Monica
la source
Ouais, je savais que busybox arrivait. Permet de voir si autre chose apparaît.
MDMoore313
4

Si vous n'avez besoin d'aucun utilitaire shell, un mkshbinaire lié statiquement (par exemple contre klibc - 130K sous Linux / i386) fera l'affaire. Vous avez besoin d'un script /linuxrcou /initou /sbin/initqui appelle simplement mksh -l -T!/dev/tty1en boucle:

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

L' -T!$ttyoption est un ajout récent mkshqui lui indique de générer un nouveau shell sur le terminal donné et de l'attendre. (Avant cela, il n'y avait qu'à -T-démoniser un programme et -T$ttyà apparaître sur un terminal mais pas l'attendre. Ce n'était pas si gentil.) L' -loption lui dit simplement d'exécuter un shell de connexion (qui lit /etc/profile, ~/.profileet ~/.mkshrc).

Cela suppose que votre terminal est de /dev/tty1remplacement. (Avec plus de magie, le terminal peut être découvert automatiquement. /dev/consoleNe vous donnera pas le contrôle total du travail.)

Vous avez besoin de quelques fichiers /devpour que cela fonctionne:

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

Le démarrage avec l'option du noyau devtmpfs.mount=1élimine le besoin d'un /devrépertoire rempli , laissez-le simplement être un répertoire vide (adapté à une utilisation comme point de montage).

Vous voudrez normalement avoir certains utilitaires (de klibc, busybox, beastiebox, toybox ou toolbox), mais ils ne sont pas vraiment nécessaires.

Vous souhaiterez peut-être ajouter un ~/.mkshrcfichier, qui configure $ PS1 et certains alias et fonctions de base du shell.

J'ai fait une fois un initrd compressé 171K (371K non compressé) pour Linux / m68k en utilisant mksh (et son exemple de fichier mkshrc) et klibc-utils uniquement. (C'était avant que -T! Ne soit ajouté au shell, cependant, il a généré le shell de connexion à la /dev/tty2place et a envoyé un message à la console indiquant à l'utilisateur de changer de terminal.) Cela fonctionne très bien.

Il s'agit d'une configuration minimale vraiment nue . Les autres réponses fournissent d'excellents conseils pour des systèmes un peu plus détaillés. C'est un vrai cas spécial.

Avertissement: je suis le développeur mksh.

mirabilos
la source
C'est une excellente réponse, merci pour le partage et aussi merci pour mksh.
JoshuaRLi
2

Petit programme init hello world étape par étape

entrez la description de l'image ici

Compilez un monde bonjour sans aucune dépendance qui se termine par une boucle infinie. 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

On ne peut pas utiliser sys_exit, sinon la panique du noyau.

Alors:

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"

Cela crée un système de fichiers avec notre monde bonjour /init, qui est le premier programme de l'espace utilisateur sur lequel le noyau s'exécutera. Nous aurions également pu ajouter plus de fichiers d/et ils seraient accessibles depuis le /initprogramme lors de l'exécution du noyau.

Ensuite, cddans l'arborescence du noyau Linux, la construction est comme d'habitude et exécutez-la dans 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"

Et vous devriez voir une ligne:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR

sur l'écran de l'émulateur! Notez que ce n'est pas la dernière ligne, vous devez donc regarder un peu plus haut.

Vous pouvez également utiliser des programmes C si vous les liez statiquement:

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

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

avec:

gcc -static init.c -o init

Vous pouvez exécuter sur du matériel réel avec une clé USB /dev/sdXet:

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

Excellente source sur ce sujet: http://landley.net/writing/rootfs-howto.html Il explique également comment utiliser gen_initramfs_list.sh, qui est un script de l'arborescence des sources du noyau Linux pour aider à automatiser le processus.

Étape suivante: configurez BusyBox pour pouvoir interagir avec le système: https://github.com/cirosantilli/runlinux

Testé sur Ubuntu 16.10, QEMU 2.6.1.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
la source