¿Cuáles son las aplicaciones mínimas del sistema de archivos raíz que se requieren para arrancar completamente Linux?


17

Es una pregunta sobre las aplicaciones de espacio de usuario, ¡pero escúchame!

Se requieren tres "aplicaciones", por así decirlo, para iniciar una distribución funcional de Linux:

  1. Bootloader: para los embebidos, normalmente es U-Boot, aunque no es un requisito difícil.

  2. Kernel: eso es bastante sencillo.

  3. Sistema de archivos raíz: no se puede iniciar desde un shell sin él. Contiene el sistema de archivos al que se inicia el núcleo y dónde initse llama formulario.

Mi pregunta es con respecto al # 3. Si alguien quisiera construir un rootfs extremadamente mínimo (para esta pregunta, digamos que no hay GUI, solo shell), ¿qué archivos / programas se requieren para arrancar en un shell?


Definir minimal. Puede usar un solo ejecutable sin nada más como se explica en: superuser.com/a/991733/128124 Solo que no puede salir o entrar en pánico, por lo que necesita un bucle infinito o una larga suspensión. Pregunta similar: unix.stackexchange.com/questions/17122/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Respuestas:


32

Eso depende completamente de los servicios que desea tener en su dispositivo.

Programas

Puede hacer que Linux arranque directamente en un shell . No es muy útil en la producción, quién querría tener un shell sentado allí, pero es útil como mecanismo de intervención cuando tienes un gestor de arranque interactivo: pasa init=/bin/sha la línea de comando del núcleo. Todos los sistemas Linux (y todos los sistemas unix) tienen un shell de estilo Bourne / POSIX /bin/sh.

Necesitarás un conjunto de utilidades de shell . BusyBox es una opción muy común; que contiene una concha y utilidades comunes de archivo y manipulación de texto ( cp, grep, ...), la configuración de red ( ping, ifconfig, ...), la manipulación del proceso ( ps, nice, ...), y varias otras herramientas del sistema ( fdisk, mount, syslogd, ...). BusyBox es extremadamente configurable: puede seleccionar las herramientas que desee e incluso las características individuales en el momento de la compilación, para obtener el compromiso de tamaño / funcionalidad adecuado para su aplicación. Aparte de sh, lo mínimo que realmente no se puede hacer nada sin es mount, umounty halt, pero sería atípica para no tener también cat, cp, mv, rm,mkdir, rmdir, ps, syncY algunos más. BusyBox se instala como un solo binario llamado busybox, con un enlace simbólico para cada utilidad.

Se llama al primer proceso en un sistema Unix normal init. Su trabajo es comenzar otros servicios. BusyBox contiene un sistema init. Además del initbinario (generalmente ubicado en /sbin), necesitará sus archivos de configuración (generalmente llamados /etc/inittab, algunos reemplazos de init modernos eliminan ese archivo pero no los encontrará en un pequeño sistema incrustado) que indican qué servicios iniciar y cuando. Para BusyBox, /etc/inittabes opcional; si falta, obtienes un shell raíz en la consola y el script /etc/init.d/rcS(ubicación predeterminada) se ejecuta en el momento del arranque.

Eso es todo lo que necesita, más allá de los programas que hacen que su dispositivo haga algo útil. Por ejemplo, en mi enrutador doméstico que ejecuta una variante OpenWrt , los únicos programas son BusyBox, nvram(para leer y cambiar la configuración en NVRAM), y las utilidades de red.

A menos que todos sus ejecutables estén vinculados estáticamente, necesitará el cargador dinámico ( ld.soque puede ser llamado por diferentes nombres dependiendo de la elección de libc y de las arquitecturas del procesador) y todas las bibliotecas dinámicas ( /lib/lib*.soquizás algunas de ellas incluidas /usr/lib) requeridas por Estos ejecutables.

Estructura de directorios

El estándar de jerarquía del sistema de archivos describe la estructura de directorios común de los sistemas Linux. Está orientado a las instalaciones de escritorio y servidor: se puede omitir mucho en un sistema embebido. Aquí hay un mínimo típico.

  • /bin: programas ejecutables (algunos pueden estar en su /usr/binlugar).
  • /dev: nodos de dispositivo (ver más abajo)
  • /etc: Archivos de configuración
  • /lib: bibliotecas compartidas, incluido el cargador dinámico (a menos que todos los ejecutables estén vinculados estáticamente)
  • /proc: punto de montaje para el sistema de archivos proc
  • /sbin: programas ejecutables. La distinción con /bines que /sbines para programas que solo son útiles para el administrador del sistema, pero esta distinción no es significativa en dispositivos integrados. Puedes hacer /sbinun enlace simbólico a /bin.
  • /mnt: útil para tener en los sistemas de archivos raíz de solo lectura como punto de montaje temporal durante el mantenimiento
  • /sys: punto de montaje para el sistema de archivos sysfs
  • /tmp: ubicación de archivos temporales (a menudo un tmpfsmontaje)
  • /usr: Contiene los subdirectorios bin, liby sbin. /usrexiste para archivos adicionales que no están en el sistema de archivos raíz. Si no tiene eso, puede hacer /usrun enlace simbólico al directorio raíz.

Archivos del dispositivo

Aquí hay algunas entradas típicas en un mínimo /dev:

  • console
  • full (escribir en él siempre informa "no queda espacio en el dispositivo")
  • log(un socket que los programas usan para enviar entradas de registro), si tiene un syslogddemonio (como el de BusyBox) leyendo de él
  • null (actúa como un archivo que siempre está vacío)
  • ptmxy un ptsdirectorio , si desea utilizar pseudo-terminales (es decir, cualquier terminal que no sea la consola), por ejemplo, si el dispositivo está conectado en red y desea telnet o ssh en
  • random (devuelve bytes aleatorios, corre el riesgo de bloqueo)
  • tty (siempre designa la terminal del programa)
  • urandom (devuelve bytes aleatorios, nunca bloquea pero puede no ser aleatorio en un dispositivo recién iniciado)
  • zero (contiene una secuencia infinita de bytes nulos)

Más allá de eso, necesitará entradas para su hardware (excepto las interfaces de red, estas no reciben entradas /dev): puertos serie, almacenamiento, etc.

Para los dispositivos integrados, normalmente crearía las entradas del dispositivo directamente en el sistema de archivos raíz. Los sistemas de gama alta tienen una secuencia de comandos llamada MAKEDEVpara crear /deventradas, pero en un sistema integrado, la secuencia de comandos a menudo no se incluye en la imagen. Si parte del hardware puede conectarse en caliente (por ejemplo, si el dispositivo tiene un puerto host USB), udev/dev debe administrarlo (es posible que aún tenga un conjunto mínimo en el sistema de archivos raíz).

Acciones de tiempo de arranque

Más allá del sistema de archivos raíz, debe montar algunos más para el funcionamiento normal:

  • procfs on /proc(casi indispensable)
  • sysfs en /sys(casi indispensable)
  • tmpfssistema de archivos activado /tmp(para permitir que los programas creen archivos temporales que estarán en la RAM, en lugar de en el sistema de archivos raíz que puede estar en flash o solo lectura)
  • tmpfs, devfs o devtmpfs en /devif dinámico (ver udev en "Archivos de dispositivo" arriba)
  • devpts on /dev/ptssi desea utilizar [pseudo-terminales (ver el comentario ptsanterior)

Puede hacer un /etc/fstabarchivo y llamar mount -a, o ejecutarlo mountmanualmente.

Inicie un demonio syslog (así como klogdpara los registros del kernel, si el syslogdprograma no se encarga de eso), si tiene algún lugar para escribir registros.

Después de esto, el dispositivo está listo para iniciar servicios específicos de la aplicación.

Cómo hacer un sistema de archivos raíz

Esta es una historia larga y diversa, así que todo lo que haré aquí es dar algunos consejos.

El sistema de archivos raíz puede mantenerse en RAM (cargado desde una imagen (generalmente comprimida) en ROM o flash), o en un sistema de archivos basado en disco (almacenado en ROM o flash), o cargado desde la red (a menudo a través de TFTP ) si corresponde . Si el sistema de archivos raíz está en RAM, conviértalo en initramfs , un sistema de archivos RAM cuyo contenido se crea en el momento del arranque.

Existen muchos marcos para ensamblar imágenes raíz para sistemas embebidos. Hay algunos consejos en las preguntas frecuentes de BusyBox . Buildroot es muy popular, ya que le permite crear una imagen raíz completa con una configuración similar al kernel de Linux y BusyBox. OpenEmbedded es otro de esos marcos.

Wikipedia tiene una lista (incompleta) de distribuciones Linux incrustadas populares . Un ejemplo de Linux embebido que puede tener cerca de usted es la familia de sistemas operativos OpenWrt para dispositivos de red (popular en los enrutadores domésticos de tinkerers). Si desea aprender por experiencia, puede probar Linux desde cero , pero está orientado a sistemas de escritorio para aficionados, en lugar de a dispositivos integrados.

Una nota sobre Linux vs kernel de Linux

El único comportamiento que se integra en el kernel de Linux es que el primer programa que se inicia en el momento del arranque. (No entraré en las sutilezas initrd e initramfs aquí). Este programa, tradicionalmente llamado init , tiene ID de proceso 1 y tiene ciertos privilegios (inmunidad a las señales KILL ) y responsabilidades ( huérfanos cosechadores ). Puede ejecutar un sistema con un kernel de Linux e iniciar lo que quiera como primer proceso, pero luego lo que tiene es un sistema operativo basado en el kernel de Linux, y no lo que normalmente se llama "Linux" -  Linux , en el sentido común del término, es un sistema operativo tipo Unix cuyo núcleo es el núcleo de Linux. Por ejemplo, Android es un sistema operativo que no es similar a Unix, sino que se basa en el kernel de Linux.


Excelente respuesta Solo mencioné el inicio en Linux en el título b / c, eso es lo que probablemente se buscará, una gran adición sobre Linux vs Linux Kernel, que necesita ser un conocimiento más extendido.
MDMoore313

@BigHomie Recuerde, la Free Software Foundation quiere que todos lo llamemos GNU / Linux, ya que en la mayoría (¿todas?) "Distribuciones de Linux" el software es GNU, aunque el núcleo es Linux (por lo tanto, GNU / Linux).
BenjiWiebe

Meh, nadie tiene tiempo para eso. ¿Entonces mi distribución debería llamarse Busybox / Linux? Lo sé, lo sé, no eres tú, Stallworth, solo desahogándose;)
MDMoore313

1
@BenjiWiebe o GNU / X11 / Apache / Linux / TeX / Perl / Python / FreeCiv . Además de RMS, todos lo llaman "Linux".
Gilles 'SO- deja de ser malvado'

@Gilles Bueno, aparte de Debian, supongo. :)
un CVn

5

Todo lo que necesita es un ejecutable vinculado estáticamente, colocado en el sistema de archivos, de forma aislada. No necesita ningún otro archivo. Ese ejecutable es el proceso de inicio. Puede ser busybox. Eso le brinda un shell y una gran cantidad de otras utilidades, todo en sí mismo. Puede ir a un sistema completamente funcional simplemente ejecutando comandos manualmente en busybox para montar el sistema de archivos raíz de lectura-escritura, crear / nodos de desarrollo, exec real init, etc.


Sí, sabía que busybox vendría. Veamos si aparece algo más.
MDMoore313

4

Si no necesita ninguna utilidad de shell, lo hará un mkshbinario enlazado estáticamente (por ejemplo, contra klibc - 130K en Linux / i386). Se necesita una /linuxrco /inito /sbin/initscript que las llamadas sólo mksh -l -T!/dev/tty1en un bucle:

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

La -T!$ttyopción es una adición reciente mkshque le dice que genere un nuevo shell en el terminal dado y espere. (Antes de eso, solo había -T-que demonizar un programa y -T$ttygenerar un terminal pero no esperarlo. Esto no fue tan agradable.) La -lopción simplemente le dice que ejecute un shell de inicio de sesión (que lee /etc/profile, ~/.profiley ~/.mkshrc).

Esto supone que su terminal es /dev/tty1, sustituto. (Con más magia, el terminal se puede descubrir automáticamente. /dev/consoleNo le dará control total del trabajo).

Necesita algunos archivos /devpara que esto funcione:

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

Arrancar con la opción del núcleo devtmpfs.mount=1elimina la necesidad de un relleno /dev, solo déjelo ser un directorio vacío (adecuado para usar como punto de montaje).

Normalmente querrás tener algunas utilidades (de klibc, busybox, beastiebox, toybox o toolbox), pero en realidad no son necesarias.

Es posible que desee agregar un ~/.mkshrcarchivo, que configura $ PS1 y algunos alias y funciones básicos de shell.

Una vez hice un initrd comprimido de 171K (371K sin comprimir) para Linux / m68k usando mksh (y su archivo mkshrc de muestra) y solo klibc-utils. (Sin embargo, esto fue antes de que se añadiera -T! Al shell, por lo que generó el shell de inicio de sesión /dev/tty2y repitió un mensaje en la consola que le decía al usuario que cambiara de terminal). Funciona bien.

Esta es una configuración mínima realmente básica . Las otras respuestas proporcionan excelentes consejos hacia sistemas algo más destacados. Este es un caso real de caso especial.

Descargo de responsabilidad: soy el desarrollador de mksh.


Esta es una gran respuesta, gracias por compartir y también gracias por mksh.
JoshuaRLi

2

Programa mínimo init hello world paso a paso

ingrese la descripción de la imagen aquí

Compile un mundo hola sin ninguna dependencia que termine en un bucle 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

No podemos usar sys_exit, o de lo contrario el kernel entra en pánico.

Luego:

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"

Esto crea un sistema de archivos con nuestro hello world en /init, que es el primer programa de usuario que ejecutará el kernel. También podríamos haber agregado más archivos d/y serían accesibles desde el /initprograma cuando se ejecute el kernel.

Luego, cden el árbol del kernel de Linux, la compilación es la habitual y ejecútela en 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"

Y deberías ver una línea:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR

en la pantalla del emulador! Tenga en cuenta que no es la última línea, por lo que debe buscar un poco más arriba.

También puede usar programas en C si los vincula estáticamente:

#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

Puede ejecutar en hardware real con un USB encendido /dev/sdXy:

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

Gran fuente sobre este tema: http://landley.net/writing/rootfs-howto.html También explica cómo usar gen_initramfs_list.sh, que es un script del árbol de fuentes del kernel de Linux para ayudar a automatizar el proceso.

Siguiente paso: configure BusyBox para que pueda interactuar con el sistema: https://github.com/cirosantilli/runlinux

Probado en Ubuntu 16.10, QEMU 2.6.1.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.