Embedded Rootfilesystem

Dieses Dokument beschreibt die Details zur Erzeugung eines minimales Linux Rootfilesystem. An der GSI wird man ueblicherweise sein Dateisystem aus bereits compilierten Komponenten mit Hilfe eines Scriptes zusammenstellen (EmbeddedFilesystemGenerator)

Allgemein

Dies Die meisten Programme (init, shell, etc) werden durch busybox zur ve

Busybox

Das Filesystem basiert auf busybox (http://www.busybox.net/). Es besteht aus einem einzelnen Binary, welches entweder gegen die uLibc, gegen glibc oder statisch compiliert wird. Dieses Binary stellt eine Vielzahl der unter Linux verwendeten tools (sh, ifconfig, dhcp client, grep, awk, vi...) zur Verfügung. Teilweise mit reduziertem Funktionsumfang.

Compilieren Busybox

Sollte ein cross compiler Verwendung finden, so sind die Umgebungsvariablen (CROSS_COMPILE, PATH) entsprechend zu initialisieren . Download und auspacken von busybox. Anschliessend erfolgt die Konfiguration ähnlich wie beim linux kernel.
# default config erzeugen
user@host$ make defconfig
user@host$ make menuconfig

busybox 19.x braucht ubi-mtd.h, das ist unter rhel5 noch nicht dabei. Daher muss man dass von seinem kernel kopieren. z.B. cp -rva /common/usr/embedded/kernel/microioc/linux-2.6.33.6-gsi02/include/mtd/ $BUSYBOX/include

i386

cd $BUSYBOX
unset CROSS_COMPILE
cp -rva /common/usr/embedded/kernel/microioc/linux-2.6.33.6-gsi02/include/mtd include
EXTRA_CFLAGS="-m32" EXTRA_LDFLAGS="-m32" make defconfig
EXTRA_CFLAGS="-m32" EXTRA_LDFLAGS="-m32" make -j 8
EXTRA_CFLAGS="-m32" EXTRA_LDFLAGS="-m32" make install

ppc

cd $BUSYBOX
unset CROSS_COMPILE
cp -rva /common/usr/embedded/kernel/microioc/linux-2.6.33.6-gsi02/include/mtd include
. /common/usr/eldk/eldk_init 6xx
make defconfig
make -j 8
make install

Ggf. eintragen des Crosscompiler prefixes unter Busybox Settings -> Build Options -> Cross Compiler Prefix

Ansonsten ist die default Konfiguration für unsere Zwecke ausreichend. Wir benötigen keine selteneren Features und haben keine engen Vorgaben was Speicherplatz betrifft, können daher unbenutzte Features verkraften.

Compilieren mit make und installieren mit make install.

Es entsteht das Verzeichnis _install. In diesem liegt die busybox und eine Reihe von symlinks, welche jeweils auf busybox verweisen.
user@host$ ls _install/
bin
linuxrc -> bin/busybox
sbin
usr

und bin sieht dann so aus
user@host$ ls _install/bin
addgroup -> busybox
adduser ->busybox
cp -> busybox
...

In der default config wird Busybox gegen die libc des Compilers gelinkt. Um das _install Verzeichnis als Basis für ein rootfilesystem zu verwenden müssen die Bibliotheken dort vorhanden sein.

abhängige Bibliotheken bestimmen

Sämtliche dynamisch gelinkten binaries brauchen immer ein ld.so (ld-linux.so, ld-2.5.so, ...)

Um für ein vorhandenes binary die verwenden Bibliotheken zu bestimmen kann man ldd oder objdumpp benutzen. Der Einsatz von ldd ist nur möglich wenn ein entsprechendes cross ldd zur Verfügung steht. Objdump kann mit sämtlichen elf basierten binaries umgehen. (bzw es kann mit allen binaries im selben binärformat wie der host umgehen und das ist üblicherweise elf).

user@host$ objdump -x _install/bin/busybox | grep NEEDED
  NEEDED      libm.so.6
  NEEDED      libc.so.6

user@host$ ${CROSS_COMPILE}ldd _install/bin/busybox
        libm.so.6 => ELDK/ppc_6xx/lib/libm.so.6
        libc.so.6 => ELDK/ppc_6xx/lib/libc.so.6
        ld.so.1 => ELDK/ppc_6xx/lib/ld.so.1

Die verwendeten Bibliotheken sind meist symlinks. Es müssen die Quellen der Links kopiert werden. Die entsprechenden symlinks erzeugt später ldconfig

ldconfig

Bibliotheken liegen im Roofilesystem unter den beiden Standardpfaden ROOTFS/lib und ROOTFS/usr/lib. Um die zugehörigen Symlinks zu erzeugen wird ldconfig verwendet

Für X86 Systeme kann das ldconfig des Host Systems verwendet werden
user@host$ /sbin/ldconfig -N -r ROOTFS

Die option -N verhindert das anlegen eines library caches. Dieser wird nicht benötigt da sich alle Bibliotheken in Standardpfaden befinden. Die Option -r sucht Biblotheken relativ zum Rootfilesystem, also nicht im Host System.

Für PPC Systeme muss entweder ein cross-ldconfig (so vorhanden) oder das ldconfig des Zielsystems ausgeführt werden. Ausführung des Zielsystem ldconfigs erfolgt mit dem Emulator qemu

user@host$ qemu-ppc SYSROOT/sbin/ldconfig -N -r ROOTFS

SYSROOT ist das zur cross-compiler toolchain gehörige sysroot Verzeichnis.

Rootfilesystem

Basis des rootfilesystems ist die busybox mit den zugehörigen Bibliotheken. Diese Dokumentation verwendet ROOTFS als Platzhalter für dieses Verzeichnis und SYSROOT als Platzhalter für das zu einem cross-compiler gehörende Systemverzeichnis. Für eldk ist dies /common/usr/eldk/ppc_6xx, für ein x86/i386 system kann der host verwendet werden /. Bei 64bit hosts ist darauf zu achten, dass die 32bit Bibliotheken verwendet werden, bei Redhat /lib bei neueren Debian Systemen /lib32.

Bibliotheken der libc werden mit versionsnamen erzeugt. So ist libdl-2.6.so die zur libc Version 2.6 gehörende dl. Als Platzhalter wird LIBCVERSION verwendet.

Erzeugen grundlegender Verzeichnisse
user@host$ cd ROOTFS
user@host$ mkdir etc         # Konfigurationsdateien
user@host$ mkdir proc        # mountpoint für proc filesystem
user@host$ mkdir tmp         # temporäre Dateien
user@host$ mkdir -p var/run  # PID Files werden hier abgelegt
user@host$ mkdir -p var/lock # lockfiles

Die folgenden Bibliotheken sind systemnah und werden von nahezu jedem Programm die auf dem System ausgeführt werden könnte benötigt (ausser der busybox...). Es ist daher sinnvoll sie direkt in das rootfilesystem zu integrieren.
  • libdl (dynamic linker)
  • libpthreads (Posix Threads)
user@host$ cp -P SYSROOT/lib/libdl-LIBCVERSION.so ROOTFS/lib
user@host$ cp -P SYSROOT/lib/libpthread-LIBCVERSION.so ROOTFS/lib

Damit ist an Programmen (fast) alles vorhanden was für ein minimales Linux benötigt wird. Fehlt die Konfiguration. Aber testweise könnte mit dieser Ramdisk unter (Verwendung von /bin/sh als init) bereits gebootet werden.

Basiskonfiguration Rootfs

Accounts

Hinzufügen von User Accounts

Ein paar Standard Accounts in ROOTFS/etc/passwd
root:PWHERE:0:0:root:/:/bin/sh
bin:*:1:1:bin:/bin:
daemon:*:2:2:daemon:/sbin:
halt:*:7:0:halt:/sbin:/sbin/halt
nobody:*:99:99:Nobody:/:

Passwort-hash erzeugen mit
user@host$ openssl passwd -1 -salt blahblah
und in ROOTFS/etc/passwd eintragen.

Standard Gruppen in ROOTFS/etc/group
root:*:0:root
bin:*:1:root,bin,daemon
daemon:*:2:root,bin,daemon
sys:*:3:root,bin
tty:*:5:
disk:*:6:root
lp:*:7:daemon
mem:*:8:
kmem:*:9:
nobody:*:99:

Inittab

Linux startet üblicherweise einen Mutterprozess namens init mit der Prozessid 1. Von diesem Prozess werden alle weiteren Anwendungen als Subprozess gestartet. Das in busybox integrierte init wertet die Datei /etc/inittab aus um Subprozesse zu starten. Im Unterschied zu einem normalen init kennt busybox keine runlevel.

Die folgende inittab führt zunächst das script etc/rc.sysinit.sh aus. Dieses wird verwendet um ein frisch gebootetes System einsatzfähig zu machen, also Dateisysteme mounten, Netzwerk konfigurieren, etc. Danach startet auf der Console ein getty um nach Benutzername und Passwort zu fragen. Der Getty Prozess wird automatisch neu gestartet sobald er sich beendet, z.B. durch logout oder crash.

Inhalt von ROOTFS/etc/inittab
::sysinit:/etc/rc.sysinit.sh
::respawn:/sbin/getty 19200 /dev/console

Damit ein root login auf der console erlaubt ist muss die Datei ROOTFS/etc/securetty existieren. Hier rudimentär damit zumindest console, echte tty, und Pseudoterminals (alter BSD und neuer UNIX98 Bauart) funktionieren.
console
tty1
tty2
ttyS0
ttyp0
ttyp1
pts/0
pts/1
...

Alle dem System bekannten Login Shells müssen in ROOTFS/etc/shells aufgeführt sein.
/bin/sh
/bin/ash
Eine bash steht nicht zur Verfügung. Ash ist die http://en.wikipedia.org/wiki/Almquist_shell Almquist-Shell.

Als ROOTFS/etc/rc.sysinit.sh sagen wir erstmal nur Hello World
#!/bin/sh
echo "rc.sysinit.sh was here"

Mit dieser Ramdisk bootet der Embedded Rechner, führt rc.sysinit.sh aus und wartet auf der Console auf einen login.

rc.sysinit.sh und Netzwerk

Grundlegene Systemscripts.

ROOFS/etc/rc.sysinit.sh wird beim booten einmalig ausgeführt. Im Ersten Teil ein paar Konfigurationen
#!/bin/sh
#
# Initialize system
echo "executing /etc/rc.sysinit.sh"

PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
export PATH

# mounting proc
/bin/mount -t proc proc /proc

# mounting rootfs rw, by default mounted ro
/bin/mount -o remount,rw /

# mounting devpts, pseudo terminals
mkdir /dev/pts
/bin/mount -t devpts none /dev/pts

Per DHCP holen wir uns eine Netzwerkadresse. Der in der busybox integrierte client heisst udhcpc. Dieser führt dhcp anfragen aus. erhaltene Antworten werden geparst und als Umgebungsvariablen an ein script übergeben.

Weiter in der ROOTFS/etc/rc.sysinit.sh
echo "bringing up the network"
# bring up network interfaces
/sbin/ifconfig lo 127.0.0.1
/sbin/ifconfig eth0 up
echo "sleeping a second, to allow interfaces to settle"
sleep 1
echo "running dhcp" # requesting the additional dhcp fields rootpath and tftp
/sbin/udhcpc -Orootpath -Otftp
# this will execute script /usr/share/udchpc/default.script
echo "finished /etc/rc.sysinit.sh"

Das script ROOTFS/usr/share/udchpc/default.script führt ein ifconfig aus, speichert die DNS Server in /etc/resolv.conf. Daneben werden noch Netzwerkrouten gesetzt, usw. Aufgrund des Umfangs hier nicht aufgeführt.

Namensauflösung (DNS, userid -> namen, etc) wird durch die libc bereitgestellt. Diese greift auf nsswitch (Nameservice Switch) zu. Nsswitch wiederum verwendet Dateien in ROOTFS/etc/ und DNS. Und für diese Funktionen werden noch ein paar Bibliotheken benötigt.
user@host$ cp -P SYSROOT/lib/libresolv-LIBCVERSION.so ROOTFS/lib
user@host$ cp -P SYSROOT/lib/libnss_{files,dns}-LIBCVERSION ROOTFS/lib

wie Nummern in Werte umgesetzt werden entnimmt nsswitch der Datei ROOTFS/etc/nsswitch.conf
passwd:     files   # accounts und benutzernamen aus /etc/passwd
group:      files    # gruppennamen aus /etc/groups
hosts:      files dns  # hostname erst aus /etc/hosts und wenn da nicht, dann dns
protocols:  files  # ip protokolle in /etc/protocols
services:   files  # bekannte/benannte ip services in /etc/services

passwd und group haben wir bereits angelegt.

ROOTFS/etc/hosts
127.0.0.1  localhost
den eigenen Hostnamen fügt das dhcp script mit ein.

ROOTFS/etc/protocols enthält protocol-name zu nummer also tcp = 6, udp = 17...
ip      0       IP      # internet protocol, pseudo protocol number
icmp    1       ICMP    # internet control message protocol
igmp    2       IGMP    # internet group multicast protocol
ggp     3       GGP     # gateway-gateway protocol
tcp     6       TCP     # transmission control protocol
pup     12      PUP     # PARC universal packet protocol
udp     17      UDP     # user datagram protocol
idp     22      IDP     # WhatsThis?
raw     255     RAW     # RAW IP interface

ROOTFS/etc/services enthält service-name zu nummer also telnet = 23, http = 80...
tcpmux          1/tcp
echo            7/tcp
echo            7/udp
...
telnet          23/tcp
...
http            80/tcp
...
AccAlarm        54321/udp                       # BEL multicasts

Damit kann der Embedded Rechner sein Netzwerk konfigurieren und danach DNS Namensauflösung.

inetd und telnet

Man kann telnet auch ohne inetd betreiben. Hier wird aber gleich der inetd mit konfiguriert.

ROOTFS/etc/inetd.conf konfiguriert die Dienste, die vom inetd gestartet werden
telnet   stream  tcp    nowait  root    /usr/sbin/telnetd /usr/sbin/telnetd -i
Telnet muss als service in ROOTFS/etc/services definiert sein.

Starten von inetd aus der ROOTFS/etc/inittab
...
::respawn:/usr/sbin/inetd -f /etc/inetd.conf
...

Damit ist der Rechner netzwerkfähig und kann per telnet erreicht werden.
Topic revision: r4 - 27 Feb 2012, ChristophHandel
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback