Next: Prozeßordnung
Up: Systemverwaltung
Previous: Der Anfang und
Linux erlaubt das Hinzufügen modularer Gerätetreiber oder Funktionen in den laufenden Kernel. Ursprünglich wurde die Modulschnittstelle für den Linux-Kernel entwickelt, um den Programmierern bessere Möglichkeiten zum Testen neuer Gerätetreiber zu bieten. Es hat sich aber schnell gezeigt, daß die Modularisierung des Kernels viel mehr Vorteile bietet, deshalb wurden nach und nach fast alle Gerätetreiber und viele Kernelfunktionen -- wenigstens potentiell -- zu Modulen umgearbeitet.
Die Distributoren können mit einem vollständig modularisierten Kernel kleinere Installationssysteme herstellen, die zudem besser an das Zielsystem angepaßt sind als die überladenen generischen Kerneldateien auf den Bootdisketten der Vergangenheit.
Die Benutzer eines modularisierten Kernels profitieren von der Schlankheitskur des Betriebssystems. Alle Gerätetreiber, die nicht ständig in Benutzung sind, können als Module ausgelagert und nur bei Bedarf in den Speicher geladen werden.
Um aus einem normalen Treiber ein Modul zu machen, müssen zunächste die Sourcen des Treibers mit dem Compiler übersetzt werden. Zusätzlich ist in der Objektdatei zu jedem Treiber jeweils eine Funktion init_module und cleanup_module erforderlich, mit der das Modul sich selbst initialisieren und wieder auflösen kann.
Die Kernelsourcen enthalten für alle Treiber, deren Modularisierung sinnvoll ist, die entsprechenden Zusatzfunktionen und können deshalb sowohl als fester Kernelbestandteil als auch als Modul übersetzt werden. Das Kernelmakefile und das Konfigurationsscript unterstützen die Modularisierung und automatisieren die eigentliche Erzeugung der ladbaren Objektdateien. Die Systemverwalterin muß lediglich beim Konfigurieren eines neuen Kernels vor dem Übersetzen der Sourcen die gewünschten Module auswählen, danach die Module mit dem Kommando ``make modules'' compilieren und schließlich durch das Kommando ``make modules_install'' in das dafür vorgesehene Verzeichnis kopieren lassen.
Bei der Auswahl der Gerätetreiber, die als Modul übersetzt werden sollen, muß auf eventuelle Abhängigkeiten der Module untereinander geachtet werden. Die kritischen Abhängigkeiten werden vom Konfigurationsscript erkannt und nötigenfalls automatisch korrigiert. Beispielsweise ist es unmöglich, gleichzeitig den Dateisystemtyp msdos fest in den Kernel einzubauen und den Basistyp fat als Modul davon zu trennen.
Die Kernelmodule sind nicht unabhängig von der Kernelversion, für die sie erzeugt wurden. Jedes Modul benutzt mehr oder weniger viele Funktionen und globale Variable, die vom laufenden Kernel exportiert werden und dem ``Kernellinker'' insmod als Symbole zum Linken zur Verfügung stehen. Diese exportierten Symbole bilden die Schnittstelle, an der die Module angekoppelt werden. Eine Liste der Symbole steht in der Pseudodatei /proc/ksyms und kann vom Systemprogramm ksyms angezeigt werden.
Wenn eine der exportierten Funktionen in einer neuen Kernelversion verändert wird, müssen alle Module, die diese Funktion benutzen, neu übersetzt werden. Aus diesem Grund werden einfacherweise die Kernelmodule für jeden Patchlevel des Kernels neu übersetzt. Das Systemprogramm insmod kann die Kernelversionsnummer, für die ein Modul übersetzt wurde, aus der Moduldatei lesen und verweigert normalerweise das Laden eines Moduls, wenn dessen Versionsnummer nicht mit der des laufenden Kernels übereinstimmt.
Dieses einfache Schema der Zuordnung von Modulen zur passenden Kernelversion ist in zweierlei Hinsicht unzureichend. Einerseits können im Zuge eigener Entwicklungsarbeit Veränderungen am Kernel und damit an der Modulschnittstelle stattfinden, ohne daß die Versionsnummer hochgezählt wird. Andererseits ist nicht jedes Modul vom Upgrade des Kernels auf den nächsten Patchlevel betroffen. Es ist durchaus möglich, daß ein Modul ohne Veränderung für viele Kernelversionen benutzt werden kann, wenn sich an den von diesem Modul benutzten Kernelfunktionen nichts ändert.Um die Abhängigkeit der Module von der Versionsnummer des Kernels zu trennen und gleichzeitig die Verwendung älterer Module mit neuen Kernelversionen zu unterstützen, können Kernel und Module mit speziellen Versionsinformationen für die Modulschnittstelle ausgestattet werden. Um dieses Feature zu nutzen, muß der Kernel mit der Option CONFIG_MODVERSIONS übersetzt werden.
Die Namen aller exportierten Kernelsymbole werden dadurch beim Übersetzten des Kernels automatisch um eine Prüfsumme erweitert, die aus dem Programmtext für die entsprechende Funktion generiert wurde. Auf diese Weise kann sichergestellt werden, daß nur solche Module zum Kernel hinzugelinkt werden, die zur aktuellen Schnittstelle passen.
Zusätzlich werden die Module nach den Typen block, cdrom, fs, ipv4, misc, net und scsi geordnet und in entsprechenden Unterverzeichnissen aufbewahrt.
Alle Programme, die mit Kernelmodulen umgehen, suchen automatisch in dem zur laufenden Kernelversion gehörenden Modulverzeichnis nach dem Modul, das geladen werden soll.
Zum Laden der Laufzeitmodule steht zunächst der Kernellinker insmod zur Verfügung, mit dem ein einzelnes Modul in den Kernel eingefügt werden kann. Wie der Linker des GCC versucht insmod, die unaufgelösten Symbole des Moduls mit den exportierten Symbolen des Kernels zu verbinden.
Wenn alle ``offenen Enden'' des Moduls erfolgreich mit dem Kernel verknüpft worden sind, veranlaßt der Linker die Initialisierung des Moduls durch die Modulfunktion init_module. Diese Funktion ist weitgehend mit der Initialisierungsfunktion identisch, die beim Booten eines fest installierten Treibers vom Kernel aufgerufen wird. Einige Treiber erlauben die Übergabe von Initialisierungsparametern, ähnlich der Bootparameter vom LILO Bootprompt.
[01] # insmod lp io=0x378 irq=7
[02] # insmod 3c503 io=0x280 xcvr=0
[03] # insmod msdos
fat_add_cluster: wrong version or undefined
fat_statfs: wrong version or undefined
fat_put_super: wrong version or undefined
...
Loading failed! The module symbols don't match your linux-2.0.18
[04] # _
Es ist möglich, daß ein Modul von einem anderen Modul abhängig ist. Das ist der Fall, wenn ein Modul Funktionen benutzt, die von einem anderen exportiert werden. In dem Beispiel werden vom Dateisystemtyp msdos Funktionen des Basistyps fat gebraucht. Um msdos als Modul laden zu können, muß deshalb das Modul für fat bereits geladen sein.
Diese Abhängigkeitsbeziehung von Modulen untereinander führt zu Ketten oder Modulstacks. Um einen solchen Stack mit insmod zu laden, müssen alle Module nacheinander in der richtigen Reihenfolge an den Kernel angehängt werden.
Um das Laden von Modulstacks zu erleichtern, gibt es das Programmpaar depmod und modprobe. Mit depmod wird eine Datei erzeugt, in der die Abhängigkeit der Module aufgezeichnet ist. modprobe hat die gleiche Aufgabe wie insmod, es sorgt aber dafür, daß der Modulstack eines abhängigen Moduls komplett geladen wird. Dazu benutzt es die von depmod erzeugte Datei.
[01] # uname -sr
Linux 2.0.18
[02] # depmod -a
[03] # depmod -a 2.0.14
*** Unresolved symbols in module /lib/modules/2.0.14/fs/vfat.o
*** Unresolved symbols in module /lib/modules/2.0.14/misc/iBCS
*** Unresolved symbols in module /lib/modules/2.0.14/net/dummy.o
*** Unresolved symbols in module /lib/modules/2.0.14/net/ppp.o
[04] # modprobe msdos
[05] # modprobe -a -t fs \*
[06] #
Das zweite Kommando führt zur automatischen Prüfung der Abhängigkeiten aller Module, die im Modulverzeichnis für den laufenden Kernel (2.0.18) enthalten sind. Das dritte Kommando testet alle Module im Verzeichnis für die Kernelversion 2.0.14 auf Abhängigkeiten. Die Warnungen zeigen, daß einige dieser Module nicht zur laufenden Kernelversion passen. Die Abhängigkeit der Module untereinander wird trotzdem korrekt erkannt.
Das vierte Kommando zeigt den Vorteil von modprobe gegenüber insmod: beim Laden eines abhängigen Moduls wird automatisch der gesamte Modulstack geladen. Im fünften Kommando des Beispiels wird schließlich gezeigt, wie mit modprobe alle Module des Typs fs, also die Dateisysteme, automatisch geladen werden können. Ohne die Angabe eines Typs würde modprobe alle Module für den laufenden Kernel laden.
Das Programm modprobe kann durch die Konfigurationsdatei /etc/conf.modules auf eine besondere Systemumgebung und für spezielle Aufgaben eingestellt werden. Für alle möglichen Einstellungen benutzt das Programm eine lange Liste von Vorgaben, die mit der Kommandozeilenoption -c angezeigt wird. Die Liste läßt sich durch zusätzliche Einträge in der Konfigurationsdatei ergänzen, und die Vorgaben können durch eigene Definitionen ersetzt werden.
# Beispiel fuer /etc/conf.modules keep path[boot]=/usr/lib/modules alias scsi_hostadapter aha1542 options lp io=0x378 irq=7 post-install lp tunelp /dev/lp1 -i 7
In den ersten Zeilen des Beispiels wird der Suchpfad erweitert, auf dem modprobe nach den angeforderten Kernelmodulen sucht. Der vorgegebene Suchpfad beginnt in /lib/modules/boot, geht dann über die Unterverzeichnisse des Modulverzeichnisses der laufenden Kernelversion und danach durch die Unterverzeichnisse von /lib/modules/default. Ohne das Schlüsselword keep wird der vorgegebene Suchpfad ersetzt und nicht, wie in diesem Fall, erweitert.
Die Definition von Aliasnamen hat für die direkte Verwendung mit modprobe nur kosmetische Bedeutung. In Kombination mit dem Kerneldämon kerneld wird über Aliasnamen die Auswahl eines bestimmten Moduls für eine allgemeinere Geräteklasse vorgenommen, wie in dem Beispiel für einen SCSI-Hostadapter gezeigt.
Beim Laden eines einzelnen Moduls kann modprobe, wie insmod, Symboldefinitionen als Argumente für die Modulinitialisierung übernehmen. Beim Laden eines Modulstacks oder einer Modulgruppe ist das nicht möglich, weil sich die Kommandozeilenargumente nicht den einzelnen Modulen zuordnen lassen. Stattdessen können in der Konfigurationsdatei für jedes Modul Optionen festgelegt werden, mit denen der Treiber beim Laden eingestellt wird.
Für aufwendigere Modulinitialisierungen besteht die Möglichkeit, vor und nach dem Laden des Moduls beliebige Kommandos ausführen zu lassen.
Die Definition von Symbolen beim Laden eines Moduls durch insmod, beziehungsweise eines der Front-Ends modprobe oder kerneld, hat die Rolle des Bootparameters, der dem Kernel zur Initialisierung eines Gerätetreibers mit dem Bootprompt übergeben werden kann. Mit Hilfe dieser Symboldefinitionen können IO-Adressen, Interruptnummern und sonstige Angaben über die installierte Hardware an den modularen Treiber übergeben werden.
Leider werden zur Übergabe der Parameter in den beiden Anwendungsfällen ganz verschiedene Methoden benutzt. Deshalb können die meisten modularen Treiber nicht einfach mit dem Bootparameter vom LILO Bootprompt initialisiert werden. Die Tabelle 5.3 zeigt die Symbole zur Initialisierung der wichtigsten modularen Treiber.
Aztech CD | aztcd | azt_port=IO-Port |
---|---|---|
Sony CDU31a | cdu31a | cdu31a_port=IO-Portcdu31a_irq=IRQ sony_pas_init=FLAG |
Phillips CM206 | cm206 | cm206=IO-Port,IRQ |
GoldStar | gscd | gscd=IO-Port |
Mitsumi MultiSession | mcdx | mcdx_drive_map=IO-Port,IRQ[,IO-Port,IRQ] |
Optics Storage | optcd | optcd_port=IO-Port |
CreativeLabs | sbpcd | sbpcd=IO-Port,Typ |
Sanyo CDR-H94A | sjcd | sjcd_base=IO-Port |
Sony CDU535 | sonycd535 | sony535_cd_base_io=IO-Port sony535_irq_used=IRQ |
Ethernet | ./net | io=IO-Port irq=IRQ |
NCR 53c7,8x | 53c7,8xx | base=Memory io_port=IO-Port irq=IRQ dma=DMA perm_options=Option |
AM53/79C974 PCI | AM53C974 | host_scsi_id=ID target_scsi_id=ID max_rate=Rate max_offset=Offset |
BusLogic | BusLogic | IO_Address=IO-Port |
NCR 53c406a | NCR53c406a | port_base=IO-Port irq_level=IRQ fast_pio=XX dma_chan=XX bios_base=XX |
Adaptec AHA152X | aha152x | aha152x=IO,IRQ,ID,Recon,Par... |
Adaptec AHA1740 | aha1740 | slot=XX base=IO-Port irq_level=IRQ |
Adaptec AHA-2940 | aic7xxx | aic7xxx_extended=1 aic7xxx_no_reset=1 aic7xxx_irq_trigger={-1,0,1} |
Future Domain 16xx | fdomain | port_base=XX bios_base=XX interrupt_level=XX this_id=XX |
Always IN2000 | in2000 | setup_strings=Liste |
Sie können mit insmod alle exportierten statischen Variablen initialisieren. Arrays können durch eine kommaseparierte Liste von Werten belegt werden. Sie finden die in Frage kommenden Symbole durch eine Untersuchung der Moduldatei mit Hilfe des Tools nm. In der Tabelle erkennen Sie die Symbole am Buchstaben d.
Einer der großen Vorteile von Kernelmodulen ist die Möglichkeit, sie wieder aus dem Kernel zu entfernen, sobald sie nicht mehr gebraucht werden. Der für diesen Zweck vorgesehene Antagonist von insmod ist das Kommando rmmod. Mit rmmod lassen sich auch ganze Modulstacks entfernen.
Bevor ein Modul aus dem Kernel herausgelöst und sein Speicherbereich freigegeben werden kann, muß es vollständig inaktiv sein. Es müssen alle Prozesse beendet sein, die das Modul benutzt haben. Auf den modularen Treiber einer Ethernetkarte dürfen auch keine Routen mehr laufen. Ist das Modul noch aktiv, während es mit rmmod entfernt werden soll, wird eine Fehlermeldung ausgegeben und das Modul unverändert im Kernel belassen.
Das Programm modprobe kann mit dem Schalter -r ebenfalls zum Löschen von Modulen eingesetzt werden. Es arbeitet dann wie rmmod.
Bei jedem Versuch, auf ein unbekanntes Gerät, ein neues Binärformat, ein fremdes Netzwerkprotokoll oder ein bisher nicht unterstütztes Dateisystem zuzugreifen, übermittelt der Kernel eine Aufforderung zum Laden des fehlenden Moduls an den Dämon. Der Dämon gibt den Auftrag an das Programm modprobe weiter, das seinerseits versucht, das passende Modul zu finden und in den Kernel einzufügen.
Wenn das nötige Modul nicht geladen werden konnte, gibt der Kernel die übliche Fehlermeldung ``No such device'' aus, sonst arbeitet er ganz normal mit dem Modul weiter.
In der Aufforderung zum Laden eines Moduls benutzt der Kernel in der Regel nicht den Namen des Moduls, sondern eine Bezeichnung der fehlenden Funktion. Wenn zum Beispiel der Floppytreiber als Modul aus dem Kernel ausgelagert wurde und ein Prozeß versucht, auf ein Diskettenlaufwerk zuzugreifen, schickt der Kernel dem kerneld eine Anfrage der Form: request_module ('block-major-2'). Hinter block-major-2 verbirgt sich der Floppytreiber, das Blockdevice mit Hauptgerätenummer 2. Die Übersetzung in dieser Bezeichnung in den Namen des Kernelmoduls wird von modprobe durch einen Aliasnamen vorgenommen. In der Liste von Vorgaben, die in modprobe ohne weiteres Zutun enthalten ist, sind die Aliasnamen für alle gängigen Kernelmodule enthalten.
Es gibt jedoch einige Module, die nicht anhand der vom Kernel abgegebenen Anfrage identifiziert werden können. Wenn zum Beispiel auf ein SCSI-Bandlaufwerk zugegriffen werden soll und außer dem Treiber für das Bandgerät auch der Treiber für den SCSI-Hostadapter als Modul geladen werden muß, fordert der Kernel nur ein Modul mit der Bezeichnung scsi_hostadapter. Der Kernel hat keine Möglichkeit festzustellen, welcher Hostadapter in den Rechner eingebaut ist.In diesem Fall muß das passende Modul durch eine entsprechende Aliasdefinition in der Datei /etc/conf.modules identifiziert werden.
Kernelmodule, die mit dem kerneld geladen wurden, sind bei einem Listing der aktiven Module durch lsmod mit dem Zusatz (autoclean) gekennzeichnet. Das bedeutet, daß diese Module automatisch entfernt werden, wenn sie länger als eine Minute nicht mehr benutzt werden.Das Linux Anwenderhandbuch