Linuxxer schrieb:
> Evtl auch mal den Kernel updaten.
Bei einer gut unterstützten Platform wie dem RPi besteht aufgrund des
guten Supports durch die bekannten Distributoren dazu meist kein Grund.
Kernel Updates kommen da direkt vom Distributor über den Package manager
zusammen mit all den anderen Updates.
Bei Plattformen, die vom Mainline Kernel https://www.kernel.org/
unterstützt werden, ist es ziemlich einfach, den Kernel selbst zu
kompilieren. Zuerst braucht man eine Konfig, in der die benötigten
Treiber für das System eingeschaltet sind. Wenn man schon eine hat, kann
man die in das /.config file im den Kernel sourcen kopieren. mit "make
menuconfig" kann man das dann noch anpassen.
Für diverse Boards gibt es bereits fertige im tree. Bei ARM z.B:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/configs
Bei ARM64 ist die Auswahl noch etwas entteuschend:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/configs
Man kann die ganze Liste der vordefinierten Configs mit "make ARCH=arm
help | grep defconfig" anschauen.
Die Configs können dan mit "make ARCH=arm defconfig
KBUILD_DEFCONFIG=name_der_defconfig" verwendet werden, um daraus die
.config zu erstellen. Um aus der .config wieder eine _defconfig zu
erstellen, kann man "make ARCH=arm savedefconfig" verwenden.
Das "ARCH=arm" ist nur nötig, wenn man cross compiliert. Wenn man den
Kernel auf dem Zielsystem kompiliert, kann man es weglassen. Dort kann
man, wenn man alle nötigen Module schon geladen hat, die Config dann
auch mit "make localyesconfig" oder "make localmodconfig" für eine
Minimalconfig erstellen.
Neben localdefconfig gibt es noch oldconfig (bestehende Konfig nehmen
und bei neuen Optionen nachfragen), defconfig (default config für
Architektur) und config (alle Optionen nachfragen, das dauert ein paar
Stunden bis Tage, wenn man alle durchliest.). Bei embeded Boards ist es
aber oft so, dass man gewisse Sachen für ein Spezifisches Board ein oder
ausstellen muss, speziell bei solchen wo man die nötigen Config Optionen
nicht kennt ist das nervig, und viele Boards und Treiber sind noch nicht
in Mainline. Hersteller von embeded Boards oder Smartphones haben oft
eigene Forks des Kernels, bis die Anpassungen in Mainline ankommen,
falls der Hersteller die überhaupt je einsendet kann es ewig dauern. In
dem Fall muss man den dann erstmal finden.
Zum kompilieren kann man dann einfach "make" eingeben. Beim Cross
compilieren muss man die Architektur und den Kompiler noch mitgeben,
also z.B. "make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-" oder "make
ARCH=arm CROSS_COMPILE=arm-linux-gnu-". Den jeweiligen Kompiler muss man
natürlich auch noch installiert haben. Für bekannte Distributionen, wie
z.B. Debian-basierte gibt es noch Targets, die den Kernel in Packete für
die Distribution verpackt. unter Debian wäre das z.B. "deb-pkg". Das
ganze Kommando währe dann also z.B. "make ARCH=arm64
CROSS_COMPILE=aarch64-linux-gnu-".
Das boot verfahren ist je nach Architektur, CPUs und Boards auch sehr
verschieden. Beim RPI z.B. wird der Kernel vom Grafikchip geladen!!!
Die meisten ARM Boards verwenden uboot um den Kernel zu laden. Dort ist
auch sehr vieles board spezifisch. Neuere Boards, besonders solche mit
arm64 Architektur benötigen gewisse Initialisierung schritte, die uboot
nicht enthält, und verwenden deshalb arm-trusted-firmware dafür als
first-stage bootloader, der dan uboot als second stage bootloader lädt,
der dann den Kernel lädt. Die arm-trusted-firmware (ATF) enthält auch
mechanismen, über die die Signatur der Bootloader mit einem Zertifikat
in einem Rom beim Boot verifiziert werden kann, und ist damit direkt
mitverantwortlich, dass man bei fast allen Smartphones als
Smartphonebesitzer den Bootloader usw. nicht ersetzen kann, also man
sein Eigentum nicht voll unter Kontrolle hat, was ich eine riesen
Schweinerei finde. Wie schwer wäre es gewesen, einen internen EEPROM
stat ROM zu nehmen, und einen Mechanismus zum Austausch der Zertifikate
der Benutzer einzubauen? Aber ich komme vom Thema ab. Wie die ATF
geladen wird, da bin ich auch noch überfragt.
Während bei PCs mit PCI und ACPI usw. die Hardware ermittelt werden
kann, verwendet Android andere Verfahren. Es werden device trees files
verwendet. Diese kommen in diversen Formen, können von uboot von einem
File geladen werden, in uboot mit ein kompiliert werden, in den Kernel
mit ein kompiliert werden, von uboot verändert werden, und uboot und der
Kernel können unterschiedliche device trees haben. In der regel braucht
man einfach eins, das zum Board passt. Bei uboot gibt es dann noch
diverse Möglichkeiten einzustellen, wie der Kernel geladen wird. Man
kann ein Script schreiben und mit uboot-mkimage in ein uboot script
umwandeln, oder die fest Einkompilieren, und vieles mehr.
Dann noch zum Starten des Systems durch den Linux kernel. in einer
intird oder initramfs wird in der regel zuerst /init mit pid 1
gestartet. Diese mounten dann die Dateisysteme und sstarten das
eigentliche init, wieder mit pid 1, mit einem exec in /init. Wird ohne
initrd/initramfs gebooted startet der Kernel statdessen /sbin/init,
/etc/init, /bin/init oder /bin/sh, jenachdem, welches er in der
Reihenfolge zuerst findet.
Der init Prozess kann auch ein Script sein. Es gibt 4 Arten, wie unter
Linux ein Programm gestartet werden kann.
1) Ein Script gibt das Program, welches dieses interpretiert, mit der
Shebang Zeile ganz am Anfang an, z.B. "#!/bin/bash" oder "#! /bin/sh"
oder "#! /bin/sh -l ". Der Abstand zwischen #! und dem Kommando wird
ignoriert. Es ist maximal ein zusätzliches Argument möglich. Das
Programm wird dann mit "/angegebenes/program argument_falls_vorhanden
pfad/mit/dem/das/script/gestartet/wurde" gestartet.
2) Bei statisch gelinkten Programmen wird das Programm in den Speicher
gemappt und direkt gestartet.
3) Bei dynamisch gelinkten elf Programmen gibt es eine .interp section,
die ein program/library angibt die statdessen vom kernel geladen wird,
und dann das eigentliche Programm in den Speicher mapt und ausführt. Das
ist der dynamic linker/loader, auch bekannt als ld.so.
4) Mit dem binfmt_misc Mechanismus kann man beliebige Dateiformate
anhand von magic numbers oder extension usw. mit einem interpreter
starten lassen. Damit kann man z.B. windows .exe direkt über wine
ausführbar machen ohne explizit "wine bla.exe" angeben zu müssen, oder
man kann es konfigurieren qemu-aarch64-static bei arm Binaries zu
verwenden, wodurch man diese dann direkt ausführen kann. So sind sogar
Chroots und Container mit komplett anderer Architektur möglich.
Unabhängig davon, welches verfahren verwendet wird, müssen beim Starten
einer Anwendung die Interpreter, Libraries und Loader einfach alle
vorhanden sein. Bei statischen Programmen hat man den Vorteil, dass man
sich um die verwendeten Libs keine Gedanken machen muss. Das einrichten
der ganzen build toolchain für eine eigene Distribution kann sehr
aufwendig sein.
Beim manuellen einrichten von Distributionen oder Images von einem
anderen System aus haben eigentlich alle Distributionen Tools und
verfahren das Bootstrapping zu vereinfachen. Gentoo bietet diverse
Tarballs an, die genug Tools beinhalten den rest zu compilieren und zu
emergen. Debian hat debootstrap, welches die wichtigsten Packete
herunterzuladen und zu entpacken. Auf diese weise kann man einfaches
Rootfs erstellen.
Linuxxer schrieb:
> Am besten wäre es wenn man C++ darauf programmiert/kompiliert.
Will man aber nur eine bestehende Distro anpassen, kann man all das
ignorieren. Dann nimmt man einfach die Distro und ändert was auch immer
man will. Bei C++ installiert man halt g++, oder wenn amn cross
compillieren will halt den aarch64-linux-gnu-g++ oder arm-linux-gnu-g++,
je nachdem, was man vor hat.