Kernelmodule laden und entladen ist einfach. Wer aber weiß, was dabei passiert, erkennt Möglichkeiten, die weit über Treiber hinausgehen. Selbst das Schreiben eigener Module ist dann keine Hexerei mehr.
Kernelmodule sind für Entwickler und Anwender gleichermaßen eine gute Idee: Die Komponenten sind klein, überschaubar und die Entwickler können bequem außerhalb des Kerneltrees Treiber entwickeln, der Anwender lädt nur die Bestandteile, die er wirklich braucht. Kernelmodule sind auch einfach zu benutzen: »insmod modul.o« – schon ist ein Treiber für ein Dateisystem oder ein Stückchen Hardware geladen. Was steckt hinter dieser scheinbar so einfachen Verwendbarkeit und wie funktioniert so ein Modul genau?
Aufgaben eines Moduls
Kernelmodule programmiert man, um Treiber für Hardware, spezielle Filesysteme oder Netzwerkprokolle zu entwickeln. Lange Zeit war beispielsweise die Funktionalität von IPv6 in zwei unterschiedlichen Implementierungen erhältlich – eine im Kernel und die andere extern. Die meisten Anwender wissen, dass sie Treiber als Module laden können, weniger bekannt ist die Möglichkeit, eigene Implementationen von Systemcalls als Module zu entwickeln. Man kann neue Systemcalls schreiben und als Kernelmodul laden oder bereits vorhandene Linux-Systemcalls neu programmieren. Lädt man diese als Modul, überschreiben die neuen Systemcalls die vorhandenen Originale; ist das Modul entladen, werden die ursprünglichen Systemcalls wieder sichtbar.
Manche Module enthalten Treiber für spezifische Executables, um beispielsweise Java-Bytecode direkt ausführen zu können: Dank des entsprechenden Moduls genügt dann ein Aufruf von » meinprogramm« statt »java meinprogramm«. Mit dem Modul »binfmt_java« lädt der Kernel einen Bytecode-Interpreter und behandelt Java-Bytecode wie ELF- oder das alte A.out-Format.
Es gibt sogar einen Mechanismus, in den Modulen des Linux-Kernels die Abhängigkeiten der Lizenz festlegen: Der Entwickler kann bestimmte Funktionen einarbeiten, damit sich nur Module, die ebenfalls unter der GPL veröffentlicht werden, von einem GPL-Modul abhängig machen.
Anwendungsfall Krypto
Viele spezielle Bedürfnisse wie Krypto-Ergänzungen und Ähnliches sind überhaupt nur mit Modulen außerhalb des Kernelbaums abzudecken: Unter der Adresse [http://loop-aes.sourceforge.net/] findet man beispielsweise die Sourcen, um ein Loopback-Device mit AES zu verschlüsseln. Verschiedene Gründe sprechen dafür, Kryptographie in einem externen Modul zu verwenden: Internationale Kryptographiegesetze, Exportregelungen oder Softwarepatente zwingen Entwickler und Distributoren immer wieder dazu, zwischen einheimischen und internationalen Versionen zu unterscheiden.
Reine Praxiserwägungen spielen ebenfalls eine Rolle: Um ein Linux-System zu betreiben, ist ein verschlüsseltes Loopback-Device keine Grundvoraussetzung und viele Anwender werden so spezielle Anwendungen nie benötigen. Externe Module verhindern außerdem, dass der Kerneltree ins Uferlose wächst und jedes Sonder-Spezial-Feature gleich im Hauptbaum landet.
Das Leben eines Moduls
Natürlich kann man nicht einfach jedes beliebige Binary in den Kernel laden. Allen Modulen ist gemeinsam, dass sie einen Lade- und Entlademechanismus enthalten. Einige Zeilen C-Code in den Sourcen versetzen etwa die Werkzeuge »insmod« und »modprobe« oder die Kernel-eigenen Lademechanismen wie »kerneld« und »kmod« in die Lage, eine kompilierte ».o«-Datei als Modul zu erkennen und in den Kernel zu laden.
Lädt man mit »insmod modul.o« ein Kernelmodul, setzt sich folgender Mechanismus in Gang: Insmod ruft den Systemcall »init_module()« auf. Der Kernel erfährt von den Adressbereichen des Moduls, indem der Systemcall »init_module()« die Init-Routine des Moduls aufruft – die ebenfalls »init_module()« heißt. Die Init-Routine des Moduls reicht dessen Adressbereich an den Systemaufruf weiter und der wiederum macht das Modul dem Kernel bekannt.
Jedes Modul enthält die Funktion »init _module()«, damit der Lademechanismus in Gang gesetzt werden kann, und die Funktion »cleanup_module()«, um ein Modul wieder zu entfernen. Der Lademechanismus hat die Aufgabe, Module in den Kernelspace zu laden, also einen Speicherbereich, der nur dem Kernel zugänglich ist. Im Gegensatz dazu existiert der Userspace, in dem alles andere seine Speicherbereiche erhält. Selbst Prozesse, die mit Root-Rechten gestartet werden, müssen den Kernelspace respektieren und erhalten Speicherplatz nur im Userspace.
Daher resultiert übrigens auch die Unterscheidung zwischen Systemcalls und den Librarycalls: Systemcalls (Manpage-Sektion 2) verwalten ihren Adressbereich im Kernelspace, Libcalls (Manpage-Sektion 3) nur im Userspace. Systemcalls sind eine klar definierte und limitierte Anzahl von Einsprungspunkten direkt in den Kernel.
Jeder Systemcall hat sein gleichnamiges Pendant in der Libc: Beim Programmieren einer Applikation verwendet man nicht den eigentlichen Syscall direkt, sondern den Wrapper aus der Libc: Unter Linux hilft ein Blick in »/usr/include/sys/syscall.h«, dahinter in »/usr/ include/bits/syscall.h« sowie »/usr/include/asm/unistd.h« weiter, um die unterschiedlichen Aufrufe und Wrapper zu verstehen.
Die Libc braucht beim Kompilieren die Liste der Syscalls aus den Kernel-Sourcen, daher wird je nach System »unistd.h« und vieles mehr kopiert oder als Symlink angelegt. Der Kernel selbst ist natürlich unabhängig von der Libc: Wer mal einen Blick in die Kernel-Sourcen wirft, findet etwa ein »kmalloc« statt eines »malloc« – der Kernel bringt seine eigenen Funktionen mit.
Ist das Modul geladen, kann es auf die exportieren Symbole des Kernels und der anderen Module zugreifen und Funktionen oder Variablen nutzen. Oft ist eine Funktionalität in mehrere Module aufgeteilt, das heißt, es existiert eine Art Basismodul, das von anderen Modulen abhängt. In »/proc/ksyms« steht die Liste aller exportierten Symbole. Man kann mit »ksyms« nur die Symbole der geladenen Module auflisten oder mit »ksyms -a« alle exportieren Symbole anzeigen, sowohl die der geladenen Module als auch die des Kernels.
Versionen und Lizenzen
Wegen dieses gegenseitigen Exports von Symbolen müssen Kernel und Module zueinander passen: Kompiliert man einen neuen Kernel, landen die Module durch »make modules« und »make modules_install« passend zur Kernelversion in »/lib/modules/ Kernelversion/«. Die Versionsabhängigkeit ist in der Sektion ».modinfo« zu erkennen. Der Aufruf
modinfo -f %{kernel_version} sound.o.gz
zeigt, dass dieses Modul für den OSS-Sound-Support mit den Header-Files der Kernelversion 2.4.18 kompiliert wurde. Passen Module und Kernel nicht zusammen, würde »insmod« festellen, dass die ».modinfo«-Sektion nicht mit der Kernelversion übereinstimmt, und den Ladevorgang abbrechen. Mit »insmod -f« (für force) kann man das Laden des Moduls erzwingen.
Da eine andere Modulversion nicht unbedingt heißt, dass ein Modul Symbole verwendet, die nicht existieren, kann man mit der Option »CONFIG_MODVERSIONS« in der Kernelkonfiguration die Versionsabhängigkeiten zu mehr Großzügigkeit überreden.
Die ».modinfo«-Sektion zeigt noch mehr Informationen beim Aufruf von »modinfo sound.o.gz« (Listing 1). Man sieht, dass das Modul unter der GPL veröffentlicht wird. Seit Kernelversion 2.4.10 kann jedes Modul mit einem entsprechenden Flag versehen werden. In »/proc/sys/kernel/tainted« steht ein 0- oder 1-Flag: Gilt ein Modul als verdorben, wirft Insmod bei »1« eine Warnung aus: Achtung, dieses Modul enthält gar keine oder eine Non-GPL Lizenz (siehe Kasten “Minimalmodul selbst gebaut”). Zusätzlich können Modulentwickler einzelne Symbole in Abhängigkeit zur GPL exportieren: GPL-only Symbole sind nur für Module sichtbar, die eine kompatible Lizenz enthalten, für alle anderen sind diese Symbole tabu.
|
Listing 1: |
|---|
01 filename: sound.o.gz 02 description: "OSS Sound subsystem" 03 author: "Hannu Savolainen, et al." 04 license: "GPL" 06 parm: dmabuf int 06 parm: dmabug int |
|
Minimalmodul selbst |
|---|
|
Ein ganz einfaches Kernelmodul, das bei Insmod und Rmmod nur ein wenig Text ausgibt, ist sehr schnell geschrieben:
#define __KERNEL__
#define MODULE
#include <linux/modversions.h>
#include <linux/module.h>
int init_module()
{
printk("Hallo Welt, hier ist dein Modul!n");
return 0;
}
void cleanup_module()
{
printk("Das Leben eines Moduls ist kurz!n");
}
»gcc -Wall -c modul.c« kompiliert den Code als Modul. Da keine Lizenzparameter im Code stehen, wirft Insmod gleich die Fehlermeldung aus:<c>@22 K_Li:Warning: loading /home/banshee/Programming/C/Modul/modul.o will taint the kernel: no license. See http://www.tux.org/lkml/#s1-18 for information about tainted modules Module modul loaded, with warnings Mit »lsmod« taucht das Modul als »modul« in der Liste der geladenen Module auf, auch »ksyms« kennt das neue Modul: Address Symbol Defined by e08b7060 __insmod_modul_S.text_L60 [modul] e08b7000 __insmod_modul_O/home/banshee/Programming/C/Modul/modul.o_M3E411C7D_V132114 [modul] e08b70a0 __insmod_modul_S.rodata_L98 [modul] Kopiert man das Modul jetzt noch in ein Unterverzeichnis in »/lib/modules/ Kernelversion/« und ruft »depmod -a« auf, kann man das neue Modul in »modules.dep« bewundern und mit »modprobe modul« laden beziehungsweise entladen. |
Module laden und entfernen
Mit »rmmod« wird ein geladenes Kernelmodul wieder entfernt. »lsmod« zeigt die geladenen Module und ihre Abhängigkeiten an (Abbildung 1). Hier ist erkennbar, wie die Module für den Soundtreiber »emu10k1« voneinander abhängen: Der Abschnitt »Used by« verweist auf die Verwendung der Module untereinander und zeigt mit dem Reference Counter, ob ein Modul verwendet wird. Nur wenn der Reference Counter »0« ist, dürfen Insmod oder Modprobe ein Modul entladen, sonst erscheint die Fehlermeldung »Device or resource busy«.
Die Parallelport-Treiber sind mit dem Autoclean-Flag versehen: Greift während eines definierten Zeitraums keine Komponente auf das Modul zu, wird es automatisch von Kerneld entladen. Dazu muss im Kernel die Option »CONFIG _KERNELD« aktiviert sein. Das Autoclean-Flag kann man Insmod als Parameter beim Laden eines Moduls mit »modprobe -k modul.o« übergeben. Das macht aber nur Sinn, wenn man auch den Kerneld verwendet.
Laut Howto kann der Kmod, den man mit »CONFIG_KMOD« im Kernel aktiviert, Module nicht selbst automatisch entladen. Stattdessen lassen sich Autoclean-fähige Module mit »rmmod -all« via Cronjob entfernen. Da Kmod im Laufe der Zeit den Kerneld ersetzen soll, kann sich das allerdings von einer Release zur nächsten ändern.
Eine bequemere Alternative zu den Einzelbefehlen »rmmod«, »lsmod« und »insmod« ist der Befehl »modprobe«. Er erkennt im Gegensatz zu Insmod die Abhängigkeiten von Modulen untereinander und lädt den kompletten Stapel in korrekter Reihenfolge.
Modprobe erfährt von den Dependencies dank Depmod: In beinahe jeder Distribution versteckt sich irgendwo in den Init-Skripten ein kleiner Aufruf: »depmod -a«. Er liest die Abhängigkeiten der Module untereinander aus und schreibt sie in die Datei »/lib/modules/ Kernel-version/modules.dep«. An ihr kann Modprobe erkennen, dass Modul B nur geladen werden kann, wenn Modul A geladen ist. »depmod -n« schreibt den Output auf die Kommandozeile statt in die Datei. Schlägt das Laden eines Moduls innerhalb des voneinander abhängigen Stapels fehl, wird der ganz Stapel komplett wieder entladen.
Listing 2 zeigt, wie Depmod Abhängigkeiten von einigen Soundmodulen in die Datei »modules.dep« einträgt. Ähnlich wie in einem Makefile legt Depmod dort fest, dass »emu10k1« und die drei Module »sound«, »soundcore« und »ac97 _codec« voneinander abhängen. »modprobe emu10k1« lädt den kompletten Stapel Module für diesen Soundtreiber und »modprobe -r« entlädt sauber alle zusammengehörenden Module.
|
Listing 2: Auszug |
|---|
01 /lib/modules/2.4.18/kernel/drivers/sound/emu10k1/emu10k1.o.gz: 02 /lib/modules/2.4.18/kernel/drivers/sound/sound.o.gz 03 /lib/modules/2.4.18/kernel/drivers/sound/ac97_codec.o.gz 04 /lib/modules/2.4.18/kernel/drivers/sound/soundcore.o.gz |
Möglichkeiten der »modules.conf«
Im Gegensatz zu Insmod liest und verarbeitet Modprobe auch die Konfigurationsdatei »/etc/modules.conf«. Statt also mühsam per Hand alle Module in korrekter Reihenfolge mit Insmod zu laden, ist es sinnvoll, in die »modules .conf« eine Sammlung von Aliases und Optionen einzutragen, die mit den korrekten Abhängigkeiten geladen werden. In die »modules.conf« gehören außerdem Parameter wie die IO-Adressen, IRQs und dergleichen mehr, die dann alle von Modprobe an Insmod übergeben und geladen werden. Bei einigen Systemen findet sich noch das veraltete »/etc/conf.modules« – immer noch von Modprobe unterstützt, aber bereits als “deprecated” markiert.
Wie welches Modul und mit welchen Parametern konfiguriert wird, steht in der jeweiligen Dokumentation für das spezielle Modul. Für einige Treiber – zum Beispiel den Video4Linux-Treiber Bttv – liegen unter »/usr/src/linux/Documentation/« eine vorkonfigurierte »modules.conf« und ein ausführlicher Text mit den passenden Optionen für Insmod. Aber auch ohne vorhandene Dokumentation kann Modinfo weiterhelfen: »modinfo -p ne.o« liefert eine Liste von Werten, die dieses (Netzwerktreiber-) Modul akzeptiert (Listing 3).
Andere Module benötigen keine weiteren Parameter und können ganz ohne »modules.conf« mit Insmod einfach beim Starten des System geladen werden. Wer die Parameter auf der Kommandozeile an Modprobe übergeben möchte, kann auf eine Konfiguration der »modules.conf« auch ganz verzichten.
|
Listing 3: |
|---|
01 io int array (min = 1, max = 4), description "NEx000 I/O base address(es),required" 02 irq int array (min = 1, max = 4), description "NEx000 IRQ number(s)" 03 bad int array (min = 1, max = 4), description "NEx000 accept bad clone(s)" |
Abhängige Module
Sowohl Depmod als auch Modprobe können verschiedene Aktionen aus der »modules.conf« verarbeiten: Abhängig vom Ladevorgang sind so genannte »pre«- und »post«-Operationen ausführbar. Wenn einige Module keine Abhängigkeiten über exportierte Symbole untereinander haben, aber trotzdem nur sinnvoll mit anderen Modulen zusammenarbeiten, lassen sich so Ladevorgänge besser koordinieren.
Denkbar sind solche Szenarien für Konfigurationen nach dem Motto “Ohne Sound sind die Videotreiber überflüssig” oder “Ich brauche dieses spezielle Filesystem nur, wenn auch bestimmte Devices vorhanden sind”. Diese Abhängigkeiten sind mit »if«- und »else«-Anweisungen noch feiner steuerbar. Aber Achtung: Wenn der Kerneld Module mit dem Autoclean-Flag über seine eigenen Entlademechanismen entsorgt, werden die Pre- und Post-Operationen zum Entfernen eines Moduls aus der »modules .conf« ignoriert.
Natürlich lassen sich auch mehrere »modules.conf«-Dateien konfigurieren und dann mit »modprobe -C«, »depmod -C« oder über die Umgebungsvariable »MODULECONF« als alternative Konfigurationsdateien laden.
So landen bei der Installation des Alsa-Soundsystem die speziellen Alsa-Module in »/lib/modules/ Kernelversion/kernel/ sound/« und entsprechenden Unterverzeichnissen, die aus Alsas Makefile stammen. Die Sound-Module, die der Kernel selbst mitbringt, stehen zwar in »/lib/modules/ Kernelversion/kernel/drivers/sound« und Unterverzeichnissen. Dank Depmod werden aber alle Unterverzeichnisse durchsucht und sowohl die normalen als auch zusätzlich installierte Dependencies von Modulen aufgelöst. Wer Alsa verwendet, konfiguriert zusätzlich einige Parameter in der »modules.conf«, die je nach System etwa aussehen wie in Listing 4.
|
Listing 4: |
|---|
01 # ALSA sound 02 alias char-major-116 snd 03 options snd snd_major=116 snd_cards_limit=1 04 alias snd-card-0 emu10k1 05 alias sound-slot-0 snd-card-0 06 alias sound-service-0-0 snd-mixer-oss 07 alias sound-service-0-1 snd-seq-oss 08 alias sound-service-0-3 snd-pcm-oss 09 alias sound-service-0-8 snd-seq-oss 10 alias sound-service-0-12 snd-pcm-oss 11 post-install snd /usr/bin/aumix -v 65 -w 65 -l 65 -p 65 |
Die Init-Skripte von Alsa rufen jetzt »modprobe -c« auf, lassen sich die aktuelle Konfiguration auflisten und übernehmen den Parameter »emu10k1«; sie rufen also letztlich »modprobe emu10k1« auf. Das Alias aus der »modules .conf« löst sich auf – und wieder setzt sich der Ladestapel in Gang. Nach erfolgreichem Laden wird der Mixer »aumix« mit passenden Parametern für den Sound-Feinschliff aufgerufen – man kann mit »post-install« auch gleich eine Fanfare abspielen lassen.

Abbildung 2: Das in 13 Zeilen C programmierte Modul (siehe Kasten unten) tut nichts anderes, als sich beim Laden brav in »/var/log/syslog« anzumelden und beim Entladen wieder zu verabschieden .
SuSE, Red Hat oder Debian bringen übrigens bereits von Haus aus eine sehr umfangreich konfigurierte »modules.conf« mit, bei der man kaum noch Hand anlegen muss. (uwo)
|
Infos |
|---|
|
[1] Alles rund um den Kernel: [http://www.tux.org/lkml/] [2] Kernelmodule-Howto: [www.linux.org/docs/ldp/howto/Module-HOWTO/index.html] [3] Howto über Kernel-Innereien: [www.linux.org/docs/ldp/howto/KernelAnalysis-HOWTO.html] [4] Kernelmodule-Programmier Howto: [www.tldp.org/LDP/lkmpg/mpg.html] [5] Handbuch zur Treiberprogrammierung unter Linux: [http://www.xml.com/ldd/chapter/book/] [6] Manpages: Insmod, Modprobe, Depmod, Modules.conf, Ksyms |
|
Die |
|---|
|
Susanne Schmidt arbeitet seit 1994 mit Linux. Sie konnte bis heute ihre Miete dank diverser Artikel und der Mitarbeit in einigen Berliner Linux-Firmen ausschließlich durch Open Source finanzieren. |







