Der Kernel - Module |
Übersicht |
Mit der Kernel-Version 1.2 hielt mit den Modulen ein Konzept in Linux Einzug, das man in der Art bislang nur von kommerziellen Unix-Systemen kannte.
Musste ein Kernel früherer Version sämtliche Treiber der von ihm zu verwaltenden Hardware fest einkompiliert haben, so wurde es nun möglich, Teile, die während des Bootens nicht zwingend notwendig sind, in separate Einheiten - die Module - auszulagern. Module lassen sich zur Laufzeit, wenn der Kernel die Eigenschaft benötigt, zum Kernel hinzufügen oder, wenn ein Treiber ausgedient hat, auch wieder aus der aktuellen Kernelkonfiguration entfernen.
Module mildern einige Probleme.
So konnte man ohne sie keinen »universellen« Kernel erzeugen, wie er z.B. bei einer Installation benötigt wird, um alle erdenklichen Hardwarekonstellationen abzudecken. Eine Installation ging fast immer mit einer anschließenden Kernelkompilation einher, eine Tatsache, die inbesondere bei Anfängern den Ruf von Linux als ein schwer zu konfigurierendes System schürte. Das modulare Design erlaubt nun automatisches Erkennen von Hardware und das dynamische Hinzufügen der Treiber.
Nicht selten schmücken heutige Rechner Soundkarte, TV-Karte, Modem, Netzwerkkarten, Adapterkarten... Nun benötigt jede davon Ressourcen, aber diese sind, vor allem in Form von Interrupts und IO-Adressen, nur begrenzt verfügbar. Nicht selten verweigerte ein Gerät die Arbeit, weil »seine« Ressourcen von einem anderen genutzt wurden. Module ermöglichen einen Ausweg. Zwar können zwei Geräte, die partout auf denselben Einstellungen beharren, nicht zur gleichzeitigen Arbeit bewegt werden, aber eine wechselseitige Verwendung ist durch Laden und Entladen der jeweiligen Treiber durchaus möglich.
Und schließlich lässt sich der Treiber in Form eines Moduls separat kompilieren. Ein neuer Kernel ist nicht nötig.
Statisch vs. modular |
Bei all den einführenden Lobeshymnen auf den modularen Kernel fragt man sich, ob die Kerneldiät auch Grenzen kennt? Ja, die gibt es!
Module müssen vor ihrer Verwendung geladen werden. Und woher nimmt der Kernel die Module? Aus einem erreichbaren Verzeichnis. Das Verzeichnis liegt auf einem Speichermedium. Der Kernel benötigt einen Treiber, um auf dieses zugreifen zu können. Also muss dieser fest in den Kernel integriert sein.
Es lassen sich weitere Beispiele von Treibern finden, die der Kernel bereits während des Ladevorgangs benötigt. Sollten Sie in die Situation gelangen, einen eigenen Kernel zu erzeugen, so stehen Sie vielfach vor der Qual der Wahl. Statisch oder modular?
Häufig nimmt das Konfigurationstool Ihnen die Entscheidung ab, indem die modulare Realisierung eines Treibers deaktiviert ist. Wo Sie die Wahl haben, sollten Sie sich fragen, ob der Kernel diesen Treiber schon beim Booten benötigt. Mounten Sie z.B. das Rootverzeichnis via NFS, so darf dieser Treiber keinesfalls als Modul vorliegen.
Alle notwendigen Eigenschaften statisch in den Kernel einzufügen, scheitert meist an der resultierende Größe dessen. Wenn der Kernel in den Hauptspeicher geladen wird, befindet sich der Prozessor noch im sogenannten Real-Modus. Somit kann nur RAM unterhalb der 1MB Grenze adressiert werden. Der Kernel muss in diesen passen!
Auch die Vermutung, ein als Modul arbeitender Treiber verrichtet seine Tätigkeit weniger effizient als sein »fest angestellter Kollege«, ist falsch. Nur der erste Zugriff auf das Gerät wird, bedingt durch den jetzt anstehenden Ladevorgang, eine minimale Verzögerung mit sich bringen. Ist er einmal drin, ist kein Unterschied mehr festzustellen.
Manueller Umgang mit Modulen |
Während der Kernel ein benötigtes Modul unverzüglich automatisch nachlädt, erfolgt das Entladen erst nach expliziter Aufforderung. D.h. entweder entledigt man sich des unnötigen Ballasts manuell oder man lässt den cron regelmäßig die Maßnahmen anstoßen.
Zum Entladen eines Moduls ist also echte Handarbeit seitens des Administrators gefragt. Aber auch das Laden kann rein manuell erfolgen. Die benötigten Werkzeuge findet man im Paket »modutils«.
Zunächst dient das Kommando lsmod zur Anzeige aller momentan geladenen Module:
user@sonne> /sbin/lsmod Module Size Used by ppp_deflate 40300 0 (autoclean) bsd_comp 4052 0 (autoclean) ppp 20876 0 (autoclean) [ppp_deflate bsd_comp] slhc 4440 0 (autoclean) [ppp] snd-pcm-oss 16872 0 (autoclean) snd-mixer-oss 4308 2 (autoclean) [snd-pcm-oss] snd-card-cs461x 2008 2 snd-timer 8064 0 [snd-pcm] snd-rawmidi 9112 0 [snd-card-cs461x] soundcore 2564 4 [snd] parport_pc 7568 0 (unused) parport 7464 0 [parport_pc] rtl8139 12224 1 (autoclean) memstat 1476 0 (unused) serial 42484 1 (autoclean) |
lsmod tut nichts anderes, als die Daten aus der Datei /proc/modules auszulesen (vergleiche Prozessdateisystem).
Das Kommando rmmod dient zum Entladen eines Moduls aus einem laufenden Kernel. Ein Modul kann nur entladen werden, wenn es zum einen nicht benutzt wird und zum anderen auch nicht von einem anderen Modul benötigt wird.
In Bezug auf obiges Beispiel (Ausgabe von lsmod) kann ein Modul nur entladen werden, wenn im Feld »Used by« unused steht:
root@sonne> /sbin/rmmod snd-timer snd-timer: Das Gerät oder die Ressource ist belegt root@sonne> /sbin/rmmod serial snd-timer: Das Gerät oder die Ressource ist belegt root@sonne> /sbin/rmmod parport_pc root@sonne> |
Eine Möglichkeit, ein Modul per Hand zu laden, bietet das Kommando insmod. Als Argument übergibt man dem Kommando mindestens den Modulnamen. Und wie heißt es nun? Alle Module werden unterhalb des Verzeichnisses /lib/modules/Kernel-Version, geordnet nach ihrer Verwendung, abgelegt. Der Bezug auf eine konkrete Kernelversion ermöglicht die gleichzeitige Unterstützung mehrerer Kernel. Sicher ist das eher für den Kernelentwickler von Bedeutung... der normale Anwender wird zwar verschiedene Kernel, selten aber Kernel unterschiedlicher Versionen verwenden.
user@sonne> ls /lib/modules/2.2.17 block fs ipv4 misc net scsi video cdrom ieee1394 ipv6 modules.dep pcmcia usb user@sonne> ls lib/modules/2.2.17/block DAC960.o cpqarray.o nbd.o xd.o |
Doch zunächst zur Arbeitsweise von insmod. Module liegen in Form von Objektcode vor (Endung .o). Dieser Objektcode enthält genau jene Symbole, die der Kernel in seiner Symboltabelle zum Zugriff auf das jeweilige Device verwendet.
insmod muss nun das Modul finden und befolgt dabei eine bestimmte Suchreihenfolge. Zunächst beachtet es den Inhalt der Shellvariablen MODULECONF, die den Pfad zu einer Konfigurationsdatei enthält. Ist die Variable nicht gesetzt, versucht das Kommando, die Informationen der Datei /etc/module.conf zu entnehmen (dazu später mehr). Ist diese Datei nicht vorhanden, sucht insmod in dem durch die Shellvariablen MODULEPATH vorgegebenen Verzeichnis. Ist diese Variable ebenso nicht existent, werden Standardverzeichnisse durchsucht. Welche das konkret sind, ist fest in das Programm einkompiliert und kann nur durch Änderung in den Quellen mit anschließender Neuübersetzung beeinflusst werden.
Der Name des Moduls muss ohne den Suffix ».o« angegeben werden, da »insmod« diesen selbsttätig ergänzt.
Von den zahlreichen Optionen seien nur die - unserer Meinung nach - wichtigen vorgestellt:
-f
-k
-p
-q
-r
-s
-v
Des Weiteren lassen manche Module die Übergabe von Parametern zu. Verbreitet sind die Angabe von IO-Port-Adressen und Interrupt-Nummern. Als Beispiel lädt der nachfolgende Aufruf das Modul für eine Realtek-Netzwerkkarte, wobei explizit der Interrupt 11 zugewiesen wird:
root@sonne> insmod rtl8139 irq=11 Using /lib/modules/2.2.16/net/rtl8139.o |
Eine solche Zuweisung überschreibt eventuelle Einstellungen in der Datei modules.conf. Stimmen die Parameter der Kommandozeile nicht, ist häufig die Ausgabe Device or resource busy zu lesen. Leider trifft die Fehlermitteilung selten den Kern der Sache...
insmod wird seine Aufgabe verweigern, wenn das zu ladende Modul die Existenz anderer Module bedingt, jene aber noch nicht geladen wurden. Per Hand ließe sich das Sorgenkind schnell hinzufügen, doch bedeutet das zum einen unnötigen Aufwand und zum anderen verbietet es die Verwendung von insmod in Skripten, da Fehler quasi vorprogrammiert sind.
Der Ausweg kommt in Form von modprobe daher, das für sich ein intelligentes insmod darstellt, da es Abhängigkeiten zwischen den Modulen entdecken und auch auflösen kann (tatsächlich ruft es insmod zum eigentlichen Laden auf; daher können Sie - analog zu insmod - Parameter an modprobe übergeben). Natürlich spekuliert modprobe nicht ins Blaue hinein, sondern konsultiert eine kleine Datenbank modules.dep, die (hoffentlich) die notwendigen Informationen bereit hält. Zu dieser Datenbank folgt im Anschluss mehr...
Stellt also modprobe fest, dass ein Modul weitere Module für seine Funktionsfähigkeit bedingt, so wird es diese in der durch die Datenbank vorgegebenen Art und Weise vor dem »eigentlichen« Modul laden. Jetzt kann es aber sein, dass die Aufnahme eines der Module scheitert (z.B. weil die angeforderten Ressourcen belegt sind). modprobe wird nun alle in den bisherigen Schritten geladenen Module entfernen, denn diese sind ja nun nutzlos geworden, da nicht alle Abhängigkeiten des Zielmoduls erfüllt sind.
Eine nützliche Eigenschaft, die vor allem während des Bootvorgangs angewandt wird, ist die Möglichkeit, alle oder auch nur das erste funktionierende Modul aus einem Verzeichnis zu laden. Da die Module nach ihrem Einsatz gruppiert sind (Netzwerkmodule liegen in einem Verzeichnis, die der Soundkarte in einem anderen...), genügt die Angabe eines Verzeichnisnamens und modprobe wird der Reihe nach die Module testen, bis das Laden eines Moduls gelingt. Liegen für alle nur erdenklichen Hardwareelemente die Module vor, so kann für jede Hardware quasi automatisch der richtige Treiber eingebunden werden.
Wiederum sei die Beschreibung der Optionen auf die in der Praxis relevanten beschränkt:
-a
-c
-k
-n
-r
-s
-t Typ
Als Beispiel soll ein Modul für die Netzwerkkarte geladen werden (zunächst eines; dann alle):
root@sonne> modprobe -t net \* root@sonne> modprobe -a -t net \* 2>&1| head -5 /lib/modules/2.2.16/net/eepro100.o: init_module: Das Gerät oder die Ressource ist belegt /lib/modules/2.2.16/net/eepro100.o: insmod /lib/modules/2.2.16/net/eepro100.o failed /lib/modules/2.2.16/net/tulip.o: init_module: Das Gerät oder die Ressource ist belegt /lib/modules/2.2.16/net/tulip.o: insmod /lib/modules/2.2.16/net/tulip.o failed /lib/modules/2.2.16/net/olympic.o: init_module: Das Gerät oder die Ressource ist belegt |
modprobe benötigt für seine Arbeit eine Datenbank modprobe.dep. Diese sollte bei den gängigen Distributionen im Verzeichnis »/lib/modules/<Kernelversion>« liegen.
Für jedes Modul sind dort die Abhängigkeiten aufgeführt. Da die Datenbank im ASCII-Format vorliegt, lässt sie sich sogar von Hand anpassen. Allerdings sollte das besser das Kommando depmod übernehmen, denn genau das ist dessen Berufung.
In den meisten Fällen genügt ein einfacher Aufruf:
root@sonne> depmod depmod: *** Unresolved symbols in /lib/modules/2.2.16/pcmcia/mpsuni_cs.o |
Eine neue Datei modules.dep sollte nun existieren (die produzierte Fehlermeldung ist unwichtig, da auf dem System kein PCMCIA vorhanden ist).
In die Versuchung depmod zu rufen, werden Sie ohnehin nur gelangen, falls Sie dem System ein neues Modul spendieren (indem Sie eines einspielen oder ein existierendes neu übersetzen). Nahezu jede Linuxdistribution hat den Aufruf in einem der Bootskripte versteckt, um die Datenbank bei jedem Bootvorgang automatisch zu aktualisieren. Dennoch sollen einige der gängigen Optionen das Thema beschließen:
-a
-A
-n
-s
Vorab: Sie sollten sich nicht wundern, falls diese Datei in Ihrem System nicht existiert. Der frühere Name dieser Konfigurationsdatei lautete conf.modules und obwohl die Benennung als veraltet verschrien ist, findet man sie noch bei zahlreichen Distributionen.
modules.conf ist sowohl für insmod, modprobe als auch für depmod interessant, da hier u.a. die Parameter enthalten sind, mit denen ein Modul zu laden ist. Sollten Sie einmal neue Hardware ins System integrieren, so sollten Sie zunächst diese Datei konsultieren und durch Aufnahme einiger Zeilen das System zur Zusammenarbeit mit ihrer Hardware überreden. Mitunter finden Sie bereits die passenden Einträge vor, die Sie nur noch »entkommentieren« müssen.
Wie in nahezu allen Konfigurationsdateien leitet auch in modules.conf das Doppelkreuz # einen Kommentar ein; lange Zeilen können mit einem Backslash »umgebrochen« werden. Die resultierenden Zeilen besitzen folgende Struktur:
Parameter=Wert
Einige Variablen werden hiermit belegt. Dabei handelt es sich um depfile=..., das den vollständigen Pfad zur Datei mit den Abhängigkeiten »modules.dep« enthält (benötigt von depmod und modprobe). Über insmod_opt=... lassen sich Kommandozeilenoptionen spezifizieren, die dem Kommando insmod mitgegeben werden.
Etwas aufwändiger sind dagegen die Angaben zur Variablen Path=..., die einen Pfad enthält, wo die Module zu suchen sind. Mit der Angabe eines optionalen »Tags« kann der Pfad für einen Modultyp explizit verändert werden. So setzt Path[net]=... die Pfade zu Modules, die die Netzwerkfunktionen betreffen. Ohne den Tag ist Path gleichbedeutend mit Path[misc].
Die Pfadangaben dürfen sogar Metazeichen der Shell enthalten; so ist die nachfolgende Zuweisung legitim:
path[boot]=/lib/modules/$(uname -r) path[misc]=/lib/modules/2.2.1? |
Alle Parameter dürfen auch wiederholt in der Datei definiert werden; die originalen Einstellungen werden damit verworfen.
keep
[add] options <Modulname> <Symbol>=<Wert>
Sollen einem Modul beim Laden besondere Optionen mitgeteilt werden, so sind diese anzugeben. Ein einleitendes add fügt die neuen Optionen zu schon bestehenden hinzu; ohne »add« wird der alte Inhalt ersetzt. Symbol und Wert sind modulabhängig und müssen der entsprechenden Kerneldokumentation entnommen werden. Als Beispiel werden Interrupt und IO-Port-Adresse der Ethernetkarte gesetzt:
options eth0 irq=11 add options eth0 io=0x378 |
Die beiden Anweisungen im Beispiel hätten auch auf eine Zeile geschrieben werden können:
options eth0 irq=11 io=0x378 |
alias <Aliasname> <Modulname>
Oft ist es einleuchtender, ein Modul unter einem Aliasnamen anzusprechen. Ein Modul kann auch unter mehreren Aliasnamen angesprochen werden. Beachten Sie, dass Optionen, die unter einen Aliasnamen definiert wurden, auch nur beim Laden des Moduls unter diesem Namen zur Geltung kommen.
alias eth0 rtl8139 |
Steht als letztes Wort einer mit alias beginnenden Zeile off, so werden Versuche, die benannten Module zu laden, immer fehlschlagen. Steht als letzter Eintrag null, so wird der Ladeversuch mit einer Erfolgsmeldung (Rückgabewert) enden, obwohl tatsächlich kein Modul geladen wurde. Dieses Vorgehen kann notwendig werden, wenn sich Module gegenseitig bedingen.
install <Modulname> <Kommando>
remove <Modulname> <Kommando>
pre-install <Modulname> <Kommando>
pre-remove <Modulname> <Kommando>
post-install <Modulname> <Kommando>
post-remove <Modulname> <Kommando>
Diese vier Einträge ermöglichen die eigentliche Definition der Abhängigkeiten. Je nach einleitendem Schlüsselwort wird in Bezug auf das angegebene Modul das Kommando vor/nach dem Laden/Entladen ausgeführt. Bedingt bspw. ein Modul ein anderes, so kann dieses in einer pre_install-Zeile wie folgt formuliert werden:
pre-install wavefront modprobe "-k" "cs4232" |
Zusätzlich lassen sich Teile der Datei in Abhängigkeit von einer Bedingung ausgeführt werden, indem folgende if-Syntax angewandt wird:
if <Bedingung> ... [elseif <Bedingung>] ... [else] ... endif |
Solche Bedingungen können der Test auf Existenz einer Datei »-f Datei«, auf ein gesetztes »autoclean«-Flag »-k« oder ein Vergleich von Werten sein. Wert ist hierbei das Resultat einer Expansion analog zum Vorgehen in der Bash. In der Voreinstellung arbeitet ein Vergleich auf Zeichenkettenbasis; durch ein vorangestelltes »-n« kann aber eine numerische Auswertung erzwungen und mit »!« das Resultat negiert werden.
Automatisches (Ent)Laden von Modulen |
Mit dem Kernel Version 2.0 wurde ein »user space« Dämon geliefert, der bei Bedarf vom Kernel angeforderte Module nachladen konnte. Dieser kerneld wurde ab Kernel 2.2 durch den nun direkt im Kernel integrierten kmod ersetzt. Die Vorteile der Realisierung als Kernelthread sind:
Die Arbeitsweise von kerneld und kmod sind identisch; beide werden, wenn der Kernel ein Modul benötigt, von diesem geweckt und versuchen ihrerseits das angeforderte Modul mit Hilfe von modprobe zu laden.
Um die Automatisierung des Modulladens verwenden zu können, ist einzig die Unterstützung durch den Kernel erforderlich. Bejahen Sie bei der Konfiguration dessen die Optionen »Enable loadable module support« und »Kernel module loader support« so wie in folgender Abbildung dargestellt.
Abbildung 1: Modul-Unterstützung im Kernel
Von sich aus entlädt der Kernel Module niemals, allerdings genügt ein Eintrag im Terminkalender des cron, um das Vorgehen zu automatisieren.
Zunächst sollte klar sein, dass ein Modul nur entladen werden kann, wenn es den Status unused aufweist (Ausgabe von "lsmod"). Des Weiteren darf kein weiteres Modul dieses Modul bedingen (keine Abhängigkeiten). Und als letzte Voraussetzung, muss ein automatisch entladbares Modul als autoclean gekennzeichnet sein. Letzteres erreicht man durch Laden eines Modules mittels:
root@sonne> insmod -k <Modulname> # oder: root@sonne> modprobe -k <Modulname> |
Das Entladen aller "autoclean unused" Module veranlasst das Kommando rmmod -a. Zumindest laut Manual Page sollte ein Aufruf genügen. Er tut es aber nicht!
Selbst wenn ein Modul als »autoclean unused« markiert ist, muss es zuvor auch verwendet worden sein, bevor »rmmod -a« dieses überhaupt in Betracht zieht! Des Weiteren sind zwei Aufrufe von »rmmod - a« zwingend erforderlich. Der erste ist der Richter: »Verurteilt zum Entladen!«; der zweite der Vollstrecker: »Raus damit!«.
Um letztlich ein nicht mehr verwendetes Modul per Cronjob zu verbannen, sähe ein Eintrag in die »crontab« wie folgt aus:
# Eintrag in /etc/crontab, der unbenutzte Module nach 10 Minuten
rauswirft (nach 5 Minuten markieren, nach 10 entladen): */5 * * * * root /sbin/rmmod -a |