Linux Kernel ============ .. index:: Linux Kernel .. _Was ist der Linux Kernel: Was ist der Linux Kernel? ------------------------- .. index:: Was ist der Linux Kernel? Der Linux-Kernel ist Kernkomponente des Linux-Betriebssystem. Es ist die Schnittstelle zwischen der Hardwareebene (Hardwarekomponenten z.B. CPU) und der Benutzerebene (Software) und nach den Bedürfnissen des Nutzers modifizierbar. Somit kommuniziert die Software über den Kernel mit der Hardware, wodurch Hardwareunterschiede nicht berücksichtigt werden müssen. Aufgaben ^^^^^^^^ Die Hauptaufgabe des Kernels ist die Kommunikation zwischen Hardware und Software. - Systemverwaltung: Aufteilung des Arbeitsspeicher - Prozessmanagement: Bestimmt die Dauer und den Zeitpunkt für die Nutzung des Prozessors - Datenträgerverwaltung: Verwaltung der Zugriffsrechte auf das Dateisystem und hilft beim organisieren des Dateisystems .. _Aufbau: Aufbau ------ .. index:: Aufbau Der Linux-Kernel besteht aus mehreren Schichten und Modulen, die zusammenarbeiten, um die Kernfunktionen des Betriebssystems bereitzustellen. Diese Schichten umfassen den Kernel-Modus und den Benutzermodus. Der Kernel-Modus erlaubt den Zugriff auf alle Systemressourcen, während der Benutzermodus eingeschränkten Zugriff hat und hauptsächlich für Anwendungen gedacht ist. Die Prozessverwaltung ist eine wichtige Komponente, die die Erstellung, Planung und Terminierung von Prozessen managt. Die Speicherverwaltung ist für die Zuweisung und Verwaltung des physischen und virtuellen Speichers verantwortlich. Das Dateisystem organisiert und verwaltet die Daten auf den Speichermedien, während die Gerätetreiber dem Kernel die Kommunikation mit der Hardware ermöglichen. Der Netzwerk-Stack handhabt die Netzwerkkonnektivität und Protokolle. Schließlich beinhaltet der architekturabhängige Code spezifische Anpassungen für verschiedene Hardwarearchitekturen. Diese modulare und flexible Struktur ermöglicht es, den Linux-Kernel an eine Vielzahl von Anforderungen und Geräten anzupassen. .. _Wie funktioniert ein Linux-Kernel: Wie funktioniert ein Linux-Kernel? ---------------------------------- .. index:: Wie funktioniert ein Linux-Kernel? Die im Kernel enthaltenen Treiber aktivieren die Hardwarekomponenten. Anschließend wird das Speichermedium, das die Linux-Installation enthält, eingebunden und das Init-System der Linux-Installation aufgerufen. Dieses Init-System lädt die Benutzeroberfläche unter Verwendung der Kernel-Treiber. Gleichzeitig wird die Kernelumgebung eingerichtet, in der die Programme ausgeführt werden. .. _Kernelarten: Kernelarten ----------- .. index:: Kernelarten Es gibt verschiedene Arten von Linux-Kerneln, die jeweils unterschiedliche Architektur- und Designprinzipien aufweisen. Der traditionelle Linux-Kernel ist monolithisch, das bedeutet, alle grundlegenden Dienste des Betriebssystems wie Speicherverwaltung, Dateisysteme und Netzwerktreiber laufen im Kernel-Modus. Dies bietet den Vorteil einer hohen Leistung und einer direkten Kommunikation zwischen den Komponenten. Im Gegensatz dazu führt ein Mikrokernel nur das Notwendigste im Kernel-Modus aus, während andere Dienste wie Dateisysteme und Netzwerke als Benutzerprozesse laufen. Dies erhöht die Stabilität und Sicherheit, kann jedoch die Leistung beeinträchtigen. Beispiele für Mikrokernel sind die Kernels von MINIX und QNX. Ein Hybridkernel kombiniert Elemente von monolithischen und Mikrokernel-Designs. Dabei laufen grundlegende Dienste im Kernel-Modus und weniger kritische Dienste im Benutzermodus. Windows NT ist ein Beispiel für einen Hybridkernel. Ein weiteres, experimentelles Konzept ist der Exokernel, bei dem der Kernel die Hardware abstrahiert und den Anwendungen direkten Zugriff ermöglicht. Dies kann die Leistung maximieren, erfordert jedoch spezialisierte Anwendungen. Jede Kernelart hat ihre eigenen Vor- und Nachteile, die je nach Anwendungsfall und Anforderungen berücksichtigt werden müssen. .. _Warum einen Linux-Kernel selbst kompilieren?: Warum einen Linux-Kernel selbst kompilieren? --------------------------------------------- .. index:: Warum einen Linux-Kernel selbst kompilieren? Gerade wenn man besondere Hardware besitzt die evtl. nur wenig Ressourcen zur Verfügung hat, ist es sinnvoll sich den Kernel selbst zu compilieren, etwa für Embedded oder sehr alte Systeme. Der default Kernel enthält viele Module, die für spezielle Hardware unnötig sind und somit nur Plattenspeicher bzw. Arbeitsspeicher verbrauchen würden. Wenn man den Kernel selbst compiliert, kann man Module nach eigenen Ansprüchen auswählen und so die Ressourcennutzung optimieren. Besitzt man beispielsweise eine bestimmte Graphikkarte kann man deren Treiber selbst mit dem Kernel abstimmen, um die optimale Performance zu erreichen. Ein weiterer Grund könnte sein, dass man den Kernel performanter machen will, indem man einen anderen Compiler verwendet als den Standard GCC. Gerade wenn man eine Intel CPU besitzt, könnte Intels C/C++ Compiler den Kernel schneller machen, da dieser spezifische Optimierungen basierend auf Features von Intel Prozessoren bietet. Zusammengefasst kann man also sagen, dass das eigenständige Compilieren einen kleineren und performanteren Kernel ergibt, der auf die eigene Systemhardware spezialisiert ist. Linux-Kernel selbst kompilieren ------------------------------- .. index:: Linux-Kernel selbst kompilieren Quell-Code ^^^^^^^^^^ Um den Linux-Kernel selber zu kompilieren wird zunächst der Quell-Code für den Kernel benötigt. Diesen kann man von vielen verschiedenen Quellen bekommen, normalerweise holt man sich ihn von https://kernel.org. Auf der Seite bekommt man einen Überblick über die verschiedenen Versionen des Kernels: .. image:: img/06/KernelVersionen.png (*Abbildung 1*) + **mainline** steht für die Kernel Version, die von Linus Torvalds gepflegt wird. + **stable** steht für das aktuelle, noch unterstützte Release (wird von Greg Kroah-Hartman gepflegt) + **longterm** steht für ein älteres Release, was keine neuen Features mehr bekommt, allerdings noch Bugfixes und Securityupdates bekommt + **linux-next** steht für den bleeding edge Kernel, das heißt, dass hier die neuesten Patches reinkommen, die später in den mainline Kernel gemerged werden. Dieser muss nicht immer kompilierbar sein. Man kann sich nun entweder einen Tarball herunterladen oder sich doch dazu entscheiden das Repository mit ``git`` zu klonen. Wenn man das neuesten Mainline Repository haben will, dann führt man folgendes aus: .. code-block:: bash git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ und wenn man ein stable Repository haben will, dann das: .. code-block:: bash git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ Die beiden Repositories unterscheiden sich in der Größe, was dem geschuldet ist, dass im Mainline Repo nur ``VERSION``, ``PATCHLEVEL`` und ``EXTRAVERSION`` geändert werden, nicht aber ``SUBLEVEL``. Version und Patchlevel heißen auch ``MAJOR`` und ``MINOR`` Versionsnummer. .. image:: img/06/Makefile_Version.png (*Abbildung 2*) ``VERSION`` spielt nur eine untergeordenete Rolle, da diese nur erhöht wird, wenn Linus Torvalds mal wieder keine Finger und Zehen mehr hat, um die ``PATCHLEVEL`` zu zählen (s. https://lkml.org/lkml/2022/10/2/255). Bei einer Erhöhung des Letzteren erhält der Kernel neue Features bzw. es werden Features deprecated, d.h. sie werden in naher Zukunft entfernt (z.B. `ReiserFS: `_). Es gibt aber auch viele verschiedene Linux Kernel, die alle auf dem Original aufbauen. Hier eine kleine Liste: + `Linux-Zen `_ + `Linux-ck `_ (Für Arch basierte Distros ein Repo) + `Linux-pf `_ + `Linux-hardened `_ Diese Forks haben alle einige kleine Änderungen zum Standart Kernel. Patches ^^^^^^^ Man kann den Kernel aber auch manuell mithilfe von Patches modifizieren. Dazu lädt man sich den zunächst den Linux Quellcode seiner Wahl runter. Anschließend lädt man sich einen Patch herunter. Wir haben uns beispielsweise für diesen `Patch `_ entschieden. Mit diesem ist es möglich die CPU-Architektur auszuwählen, für die GCC den Code optimieren soll. Zunächst Tarball und Patch holen; Tarball extrahieren: .. code-block:: bash $ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.33.tar.xz && tar -xvf linux-6.6.33.tar.xz $ wget https://raw.githubusercontent.com/graysky2/kernel_compiler_patch/master/more-uarches-for-kernel-6.1.79-6.8-rc3.patch In das Verzeichnis wechseln: .. code-block:: bash $ cd linux-6.6.33 Und den Patch anwenden: .. code-block:: bash $ patch -p1 -i ../more-uarches-for-kernel-6.1.79-6.8-rc3.patch Makeopts und Maketargets ^^^^^^^^^^^^^^^^^^^^^^^^ Der Linux Kernel wird mithilfe von Makefiles kompiliert, d.h. man kann theoretisch einfach nur ``make`` in die Konsole tippen und man erhält ein fertiges Kernel Image. Es gibt aber auch die Möglichkeit mit den ``Makeopts`` den Kompilierprozess zu steuern. ``Makeopts`` sind im Grunde nur Umgebungsvariablen, die in der Makefile ausgewertet werden. Eine der wichtigsten ist ``-j``. Damit wird gesteuert, wie viele Threads benutzt werden sollen, um den Kernel zu kompilieren. Beispiel: .. code-block:: bash # Kernel wird mit 32 Threads gebaut $ make -j 32 # Kernel wird mit allen verfügbaren Threads gebaut $ make -j $(nproc) Um den Kernel mit LLVM und nicht mit GCC zu kompilieren, setzt man ``LLVM=1`` in die Makeopts: .. code-block:: bash $ make LLVM=1 Man kann den Kernel auch für andere Architekturen kompilieren. Dazu muss zunächst die Architektur und einen Cross Compiler einstellen. .. code-block:: bash # Beispiel für einen ARMv8 Prozessor $ make CROSS_COMPILE=aarch64-unknown-linux-gnu- ARCH=arm64 Benutzt man LLVM, wird ``CROSS_COMPILE=`` nicht benötigt: .. code-block:: bash # Beispiel für einen ARMv8 Prozessor $ make LLVM=1 ARCH=arm64 Makeopts können auch als Umgebungsvariablenexportiert werden, d.h. man muss sie nicht jedes mal mit angeben, wenn man ``make`` aufruft. Beispiel: .. code-block:: bash # Beispiel für einen ARMv8 Prozessor $ make LLVM=1 ARCH=arm64 ist dasselbe wie: .. code-block:: bash # Beispiel für einen ARMv8 Prozessor $ export LLVM=1 ARCH=arm64 $ make Mit den Maketargets definiert man, was genau gemacht werden soll. Die Wichtigsten sind: + **all** Damit werden alle Targets gebaut, die in ``make help`` mit einem ``*`` markiert sind. + **clean** Löschen aller übersetzten C Dateien + **mrproper** Wie ``clean``, aber zusätzlich Löschung aller Backup und Config Dateien + **defconfig** Erstellung einer Standart Konfigurationsdatei, basierend auf der Hostarchitektur + **install** Installieren des Kernels auf das Hostsystem + **modules_install** Installieren der Module auf das Hostsystem Kernel-Config ^^^^^^^^^^^^^ Nachdem man nun den Quell-Code hat, die Patches seiner Wahl eingespielt und seine ``Makeopts`` exportiert hat, geht es nun ans Kompilieren. Zunächst wird eine Konfigurationsdatei benötigt, in der steht, was in den Kernel kommt, was als Modul kompiliert wird und was ausgelassen wird. Eine Standartdatei kann mit dem Maketarget ``defconfig`` erstellt werden. .. code-block:: bash $ make defconfig Diese wird anschließend in das Root-Directory des Repos als ``.config`` gespeichert. Will man diese nun bearbeiten, kann man dies grafisch machen mit: .. code-block:: bash $ make xconfig Oder terminalbasiert mit: .. code-block:: bash $ make menuconfig Mit einem Texteditor sollte man die ``.config`` niemals bearbeiten, da viele Optionen Abhängigkeiten besitzen und die Config Programme diese automatisch auflösen. .. image:: img/06/Menuconfig.png (*Abbildung 3*) Das ist die Ansicht, die man sieht, wenn man ``menuconfig`` startet. Mit den Pfeiltasten lässt sich das Menü navigieren, mit **Enter** steigt man in das Menü hinein, mit **Escape** kommt man wieder raus. Mit **?** erfährt man mehr über den ausgewählten Menüpunkt. Will man etwas suchen, geht das mit **/** und einem Suchbegriff. Config Status ''''''''''''' Eine Option kann man mit **Leertaste** togglen. Dabei gibt es 2 verschiedene Arten von Optionen: .. code-block:: menuconfig [*] Option 1 < > Option 2 Option 3 ``Option 1`` kann man nur in den Kernel einbauen oder ganz rauslassen und ``Option 2`` lässt sich zusätzlich als Modul bauen, welches man später vom Userspace nachladen kann. Ein Sternchen steht dafür, dass die Option in das Kernel Image reingebaut wird und bei einem **M** wird die Option als Modul gebaut. Konfiguration ''''''''''''' Nun kann man den Kernel konfigurieren. Den Patch, den wir eingespielt haben, finden wir als Konfiguration unter **Processor type and features CPU-Type**. Dort findet man unter **Processor family** eine kleine Liste von CPUs, für die GCC den Code optimiert. Standartmäßig ist ``Generic-x86-64`` angegeben, was vollkommen ausreichend ist und von den meisten CPUs heute ausgeführt werden kann. Vor dem Patch: .. image:: img/06/FamilyVorher.png (*Abbildung 4*) Nach dem Patch: .. image:: img/06/FamilyNachher.png (*Abbildung 5*) Man sieht, dass die Liste um einiges größer geworden ist und man auch herstellerspezifische CPUs auswählen kann. Unter **General architecture-dependent options** findet sich hier die Option **Link Time Optimization (LTO)**. In dieser Liste kann man normalerweise nichts auswählen, kompiliert man den Kernel jedoch mit LLVM, kann man hier einstellen welche LTO man haben will. LTO sorgt für eine Verkleinerung des Kernelimages sowie weitere Optimierungen zur Link-Zeit. .. image:: img/06/Architecture.png (*Abbildung 6*) Unter **Device Drivers** kann man beispielsweise die Unterstützung für Hardware deaktivieren, die man sowieso nicht im System eingebaut hat. Dadurch wird das Kernelimage kleiner und verbraucht weniger Arbeitsspeicher. Wenn man aber Hardware auf seinem Rechner hat die nicht in der ``defconfig`` aktiviert wurde, kann man das hier jetzt machen. Kompilieren ^^^^^^^^^^^ Wenn man nun seinen Kernel-Config fertig konfiguriert hat, kann man nun seinen Kernel kompilieren. Dazu lediglich(**Makeopts** nicht vergessen!): .. code-block:: bash $ make Wenn man den Kernel nun auf seinem eigenen Rechner installieren will: .. code-block:: bash # Wichtig: Als Root ausführen! root~# make install Anschließend die Konfigurationsdatei vom Bootloader neu generieren(bei den Meisten ist das GRUB): .. code-block:: bash root~# grub-mkconfig -o /boot/grub/grub.cfg .. _fazit: Fazit ----- .. index:: Fazit In den meisten Fällen lohnt es sich nicht den Linux Kernel selber zu kompilieren, da der Performancezuwachs oft nur sehr gering ist. Auch die Geräteunterstützung der Distro Kernel ist im Normalfall so gut, dass alle Geräte unterstützt werden. Nur in seltenen Fällen oder im Embedded Bereich lohnt sich der Aufwand, da diese meistens seltene Hardware benutzen. .. _lit: Literatur --------- - Linux-Kernel geeksforgeeks: https://www.geeksforgeeks.org/kernel-in-operating-system/ (06.06.2024). - Basiswissen zum Linux-Kernel: https://www.heise.de/select/ct/2017/6/1489331084099960 (06.06.2024). - Benefits of Building the Linux Kernel: https://www.baeldung.com/linux/kernel-building-benefits (06.06.2024). - Kernel LLVM https://docs.kernel.org/kbuild/llvm.html (12.06.2024) - Kernel Patches https://docs.kernel.org/process/applying-patches.html (12.06.2024)