Die Realisierung von Treibern in Modulform hat nicht nur – da sie die ständigen Reboots vermeiden helfen – für die Entwicklung selbst Vorteile. Insbesondere während des normalen Betriebs sparen sie Ressourcen; schließlich sind nur die Treiber geladen, die auch benötigt werden.
Da jedoch die Auswahl, das Laden und auch das Konfigurieren des Treibers dem normalen Nutzer nicht zugemutet werden können, muss dieser Prozess automatisiert werden. In Linux sind dazu eine Reihe von Mechanismen eingebaut worden, die
einen Treiber auswählen, wenn eine Applikation auf eine Hardware zugreifen möchte, deren Treiber noch nicht geladen ist (modprobe) und
einen Treiber auswählen, wenn eine neue Hardware erkannt wird (hotplug).
Greift eine Applikation auf eine Hardware zu, für die bisher kein Treiber geladen ist, startet der Kernel zunächst auf Applikationsseite modprobe (siehe Abbildung Automatisches Laden von Treibern). Modprobe bekommt vom Kernel mitgeteilt, auf welches Gerät, genauer auf welche Major-Nummer und auf welchen Gerätetyp (Block- oder Chardevice) zugegriffen werden soll. Der Treiberentwickler kann den Namen des Programms (modprobe) über die Datei /proc/sys/kernel/modprobe auslesen bzw. einstellen. Damit modprobe den zugehörigen Treiber laden und betriebsbereit bekommen kann, muss es:
den zugehörigen Treiber,
mögliche Optionen,
abhängige Module (die beispielsweise vorher zu laden sind) und
notwendige User-Programme zur Konfiguration oder auch zum normalen Betrieb des Gerätes kennen.
All diese Informationen sind in der Datei /etc/modprobe.conf abgelegt. Die Syntax der Datei ist einfach. In jeder Zeile ist ein Kommando spezifiziert – mit Ausnahme der Leerzeilen und der mit Hashmark (#) beginnenden Kommentarzeilen.
Folgende Kommandos sind definiert:
alias char-major-126 mydriver
Über das alias-Kommando kann einem Modul ein zweiter Name gegeben werden. Dieses Kommando wird beispielsweise genutzt, um die vom Kernel generierten Gerätenamen (die bei zeichenorientierten Geräten im Wesentlichen aus der Majornumber bestehen) den eigentlichen Modulen zuzuordnen.
options sb io=0x220 irq=5 dma=1
Über diesen Befehl können den einzelnen Modulen die Optionen zugeordnet werden, die beim Laden dem Modul mit übergeben werden sollen. Die Optionen selbst sind natürlich treiberspezifisch.
install serial /sbin/modprobe --ignore-install serial && { /etc/init.d/setserial modload > /dev/null 2> /dev/null }
Anstatt das Modul zu laden, führt modprobe das angegebene Kommando aus. Dadurch können Konfigurationen über Systemprogramme oder auch Daemonen gestartet werden. Das Beispiel zeigt, wie die serielle Schnittstelle direkt nach dem Laden mit Hilfe des Kommandos /etc/init.d/setserial konfiguriert wird. Zuvor ist das Modul serial geladen worden. Die Option --ignore-install verhindert, dass das Install-Kommando rekursiv immer wieder aufgerufen wird. Mit Hilfe dieser Option wird das Modul also wirklich mit den in modprobe.conf definierten Optionen geladen.
remove serial { /etc/init.d/setserial modsave > /dev/null 2> /dev/null }; /sbin/modprobe -r --ignore-remove serial
Dieses Kommando arbeitet wie das Install-Kommando, wird aber aufgerufen, wenn ein Modul wieder entladen werden soll. Im Beispiel wird vor dem eigentlichen Entladen des Moduls serial die aktuelle Konfiguration mit Hilfe des Kommandos /etc/init.d/setserial gerettet. Die Option --ignore-remove verhindert das rekursive Aufrufen des Kommandos.
include usb.modprobe
Über dieses Kommando können – ähnlich wie bei C – Dateien eingefügt werden.
Die Module selbst werden bei der Installation eines Kernels in einem Verzeichnis mit dem Namen /lib/modules/<Kernelversion>/ abgelegt. Hier spannt sich ein Baum auf, der für die unterschiedlichen Subsysteme (z.B. Netzwerk) eigene Verzeichnisse enthält, die schließlich die Module bzw. Treiber enthalten. Modprobe nutzt im Übrigen zum eigentlichen Laden des Moduls das in diesem Buch verwendete Programm insmod.
Gibt es zwischen Modulen Abhängigkeiten (zum Beispiel bei geschachtelten Modulen, bei denen das eine Modul Funktionalitäten bietet, auf die das andere Modul aufsetzen kann), nimmt modprobe darauf Rücksicht. Die Abhängigkeiten sind in der Datei modules.dep unterhalb des Modulverzeichnisses /lib/modules/<Kernelversion>/ beschrieben. Die Datei wird mit Hilfe des Programms depmod erstellt.
Auch der zweite Fall, dass der Kernel für ein neues Gerät einen Treiber sucht, wird unterstützt. Identifiziert der Kernel bzw. eines seiner Subsysteme ein neues Gerät, wird das hotplug-Programm aufgerufen. Der Name des Programms kann durch Schreiben der Datei (/proc/sys/kernel/hotplug) festgelegt werden. Als Parameter bekommt dieses Programm (hotplug) die Hardware- respektive Geräte-Identifikation (siehe beispielsweise PCI) übergeben.
Aufgrund dieser Geräte-Identifikation kann hotplug zunächst das Subsystem (z.B. PCI oder USB) ermitteln, über welches das Gerät angekoppelt ist. Für jedes Subsystem existiert ein Skript, das aufgerufen wird und den zu ladenden Gerätetreiber bestimmt (siehe Abbildung Hotplug-Mechanismus). Die Skripte selbst befinden sich unterhalb des Verzeichnisses /etc/hotplug/ und werden nach dem Subsystem und dem Zusatz ».agent« benannt (z.B. »pci.agent« oder »usb.agent«).
Die Bestimmung des Gerätetreibers erfolgt ebenfalls aufgrund der Geräte-Identifikation. Sämtliche im System bekannten Geräte sind dazu in einer Tabelle zusammen mit dem Namen des zugehörigen Treibers aufgelistet. Diese Tabelle wird bei der Generierung des Kernels automatisch erstellt. Jeder Treiber muss seinen Beitrag zur Erstellung der Tabelle dergestalt leisten, dass er die Geräte, für die er zuständig ist, bekannt gibt. Hierzu dient das Makro »MODULE_DEVICE_TABLE«. Diesem werden zwei Parameter übergeben. Der erste Parameter gibt das Subsystem an. Gegenwärtig stehen die folgenden Subsysteme zur Auswahl:
pci
usb
isapnp
parisc
ccw (S390)
input
pnp
pnp_card (Sound)
ieee1394
Der zweite Parameter ist die Liste mit den unterstützten Geräte-Identifikationen. Der Aufruf von MODULE_DEVICE_TABLE sieht damit beispielhaft folgendermaßen aus:
static const struct pci_device_id __devinitdata mm_pci_ids[] = { { .vendor = PCI_VENDOR_ID_MICRO_MEMORY, .device = PCI_DEVICE_ID_MICRO_MEMORY_5415CN, }, { .vendor = PCI_VENDOR_ID_MICRO_MEMORY, .device = PCI_DEVICE_ID_MICRO_MEMORY_5425CN, }, { .vendor = PCI_VENDOR_ID_MICRO_MEMORY, .device = PCI_DEVICE_ID_MICRO_MEMORY_6155, }, { .vendor = 0x8086, .device = 0xB555, .subvendor=0x1332, .subdevice=0x5460, .class = 0x050000, .class_mask=0, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(pci, mm_pci_ids); |
Das Makro MODULE_DEVICE_TABLE sorgt dafür, dass die Geräteidentifikationen vom Linker so zum Treiber gebunden werden, dass sie vom Programm depmod ausgewertet werden können. Depmod erstellt aus dieser Information unterhalb des Verzeichnisses »/lib/modules/<Kernelversion>/« für jedes Subsystem eine Datei, die mit dem String »modules« beginnt und eine Erweiterung hat, die mit dem Namen des Subsystems und dem String »map« endet. Beispiel: »modules.pcimap« oder »modules.usbmap«
Exportiert ein Treiber die von ihm bedienten Geräte nicht auf diese Weise, kann eine Tabelle im Hotplug-Verzeichnis (/etc/hotplug/) immer noch von Hand erstellt werden. Solche Listen heißen dann beispielsweise »usb.handmap« oder »pci.handmap«.
Darüber hinaus gibt es aber auch die entgegengesetzte Situation, dass ein Modul nicht geladen werden soll, obwohl ein entsprechender Eintrag in den »Maps« vorhanden ist. In diesem Fall wird das Modul in die Datei »blacklist« (ebenfalls im Hotplug-Verzeichnis zu finden) eingetragen.
Zurück | Zum Anfang | Weiter |
Treiber parametrieren | Nach oben | Kernel Build System |