Kapitel 1. Einleitung

Computersysteme bestehen aus einer Anzahl unterschiedlicher Hard- und Software-Komponenten, deren Zusammenspiel erst die Abarbeitung komplexer Programme ermöglicht. Zu den Hardware-Komponenten gehört beispielsweise die eigentliche Verarbeitungseinheit, der Mikroprozessor mit dem Speicher, aber auch die so genannte Peripherie, wie Tastatur, Maus und Monitor. Diese Peripherie wird über Hardwareschnittstellen an die Verarbeitungseinheit angeschlossen. So wird ein Drucker meist über ein Parallel-Interface angeschlossen, der Monitor über eine Grafikkarte, die Tastatur und die Maus über serielle Schnittstellen. Zudem haben sich eine Reihe von Universalschnittstellen, wie beispielsweise PCI (Peripheral Component Interconnect) oder USB (Universal Serial Bus) etabliert.

Zu den Softwarekomponenten gehören das BIOS, das den Rechner nach dem Anschalten initialisiert, und das Betriebssystem. Das Betriebssystem koordiniert sowohl die Abarbeitung der Applikationen als auch die Zugriffe auf die Peripherie. Vielfach ersetzt man in diesem Kontext den Begriff Peripherie durch Hardware oder einfach durch Gerät, so dass das Betriebssystem den Zugriff auf die Hardware bzw. die Geräte steuert. Dazu muss es die unterschiedlichen Geräte kennen bzw. es muss wissen, wie auf diese Geräte zugegriffen wird. Derartiges Wissen ist innerhalb des Betriebssystems in den Gerätetreibern hinterlegt. Sie stellen damit als Teil des Betriebssystemkerns die zentrale Komponente für den Hardwarezugriff dar. Ein Gerätetreiber ist eine Softwarekomponente, die aus einer Reihe von Funktionen besteht. Diese Funktionen wiederum steuern den Zugriff auf das Gerät.

Für jedes unterschiedliche Gerät wird ein eigener Treiber benötigt. So gibt es beispielsweise jeweils einen Treiber für den Zugriff auf die Festplatte, das Netzwerk oder die serielle Schnittstelle.

Da das Know-how über das Gerät im Regelfall beim Hersteller des Gerätes und nicht beim Programmierer des Betriebssystems liegt, sind innerhalb des Betriebssystemkerns Schnittstellen offengelegt, über die der Treiber für das Gerät integriert werden kann. Kennt der Treiberprogrammierer diese Schnittstellen, kann er seinen Treiber erstellen und den Anwendern Zugriff auf die Hardware ermöglichen.

Der Anwender selbst greift auf die Hardware über ihm bekannte Schnittstellen zu. Bei einem Unix-System ist der Gerätezugriff dabei auf den Dateizugriff abgebildet. Jeder Programmierer, der weiß, wie er auf normale Dateien zugreifen kann, ist imstande, auch Hardware anzusprechen.

Für den Anwender eröffnen sich neben dem einheitlichen Applikations-Interface noch weitere Vorteile. Hält sich ein Gerätetreiber an die festgelegten Konventionen zur Treiberprogrammierung, ist der Betriebssystemkern in der Lage, die Ressourcen zu verwalten. Er stellt damit sicher, dass die Ressourcen – wie Speicher, Portadressen, Interrupts oder DMA-Kanäle – nur einmal verwendet werden. Der Betriebssystemkern kann darüber hinaus ein Gerät in einen definierten, sicheren Zustand überführen, falls eine zugreifende Applikation beispielsweise durch einen Programmierfehler abstürzt.

Treiber benötigt man jedoch nicht nur, wenn es um den Zugriff auf reale Geräte geht. Unter Umständen ist auch die Konzeption so genannter virtueller Geräte sinnvoll. So gibt es in einem Unix-System das Gerät /dev/zero, das beim lesenden Zugriff Nullen zurückgibt. Mit Hilfe dieses Gerätes lassen sich sehr einfach leere Dateien erzeugen. Auf das Gerät /dev/null können beliebige Daten geschrieben werden, sämtliche Daten werden vom zugehörigen Treiber weggeworfen. Dieses Gerät wird beispielsweise verwendet, um Fehlerausgaben von Programmen aus dem Strom sinnvoller Ausgaben zu filtern.

Zur Erstellung eines Gerätetreibers ist weit mehr als nur das Wissen um Programmierschnittstellen im Kernel notwendig. Man muss sowohl die Möglichkeiten, die das zugrunde liegende Betriebssystem bietet, kennen, als auch die prinzipiellen Abläufe innerhalb des Betriebssystemkerns. Eine zusätzliche Erfordernis ist die Vertrautheit mit der Applikationsschnittstelle. Das gesammelte Know-how bildet die Basis für den ersten Schritt vor der eigentlichen Programmierung: die Treiberkonzeption.

Ziel dieses Buches ist es damit,

Scope

Auch wenn viele der vorgestellten Technologien unabhängig vom Betriebssystem bzw. von der Linuxkernel-Version sind, beziehen sich die Beispiele und Übungen auf den aktuellen Linux-Kernel 2.6.

Sämtliche Beispiele sind auf einem Debian-Linux und dem Kernel 2.6.5 getestet worden. Welche Distribution – ob Debian (pur oder in der Knoppix-Variante), SuSE, Redhat oder Mandrake – dabei zum Einsatz kommt, spielt im Grunde aber keine Rolle. Gerätetreiber sind abhängig von der Version des Betriebssystemkerns, nicht aber abhängig von der verwendeten Distribution (Betriebssystemversion).

Das Gleiche gilt bezüglich des Einsatzfeldes. Dank seiner hohen Skalierbarkeit ist Linux das erste Betriebssystem, das in eingebetteten Systemen, in Servern, auf Desktop-Rechnern oder sogar auf der Mainframe läuft. Die vorliegende Einführung deckt prinzipiell alle Einsatzfelder ab. Dabei spielt es keine Rolle, ob es sich um eine Einprozessormaschine (Uniprocessor System, UP) oder um eine Mehrprozessormaschine (Symmetric Multiprocessing, SMP) handelt.

Zu einer systematischen Einführung in die Treiberprogrammierung gehört ein solider theoretischer Unterbau. Dieser soll im folgenden Kapitel gelegt werden. Wer bereits gute Betriebssystem-Kenntnisse hat und für wen Begriffe wie Prozess-Kontext und Interrupt-Level keine Fremdwörter sind, kann diesen Abschnitt überspringen. Im Anschluss werden die Werkzeuge und Technologien vorgestellt, die zur Entwicklung von Treibern notwendig sind.

Bevor mit der Beschreibung des Treiberinterfaces im Betriebssystemkern begonnen werden kann, muss das Applikations-Interface zum Treiber hin vorgestellt werden. Denn was nützt es, einen Gerätetreiber zu schreiben, wenn man nicht im Detail weiß, wie die Applikation später auf den Treiber zugreift? Immerhin muss die von der Applikation geforderte Funktionalität im Treiber realisiert werden.

Das folgende Kapitel beschäftigt sich schließlich mit der Treiberentwicklung als solcher. Hier werden insbesondere die Funktionen eines Treibers behandelt, die durch die Applikation aufgerufen werden.

Darauf aufbauend werden die Komponenten eines Treibers behandelt, die asynchron zu einer Applikation im Kernel ablaufen. Stichworte hier: Softirqs, Tasklets, Kernel-Threads oder auch Workqueues.

Mit diesen Kenntnissen können bereits komplexere Treiber erstellt werden; Treiber, die sich jetzt noch harmonisch in das gesamte Betriebssystem einfügen sollten. Diese Integration des Treibers ist folglich Thema eines weiteren Kapitels.

Neben den bisher behandelten Treibern für zeichenorientierte Geräte (Character-Devices) werden noch Treiber für Blockgeräte, für USB-Geräte und für Netzwerkkarten vorgestellt.

Einen Treiber zu entwickeln, ist die eine Sache – gutes Treiberdesign eine andere. Dies ist Thema des letzten Kapitels.

Im Anhang schließlich finden sich Hinweise zur Generierung und Installation des Kernels, zur Portierung (2.4 auf 2.6) und eine Referenzliste der wichtigsten Funktionen, die im Kontext der Treiberentwicklung eine Rolle spielen.

Notwendige Vorkenntnisse

Das vorliegende Buch ist primär als eine systematische Einführung in das Thema gedacht. Grundkenntnisse im Bereich der Betriebssysteme sind sehr vorteilhaft. Kenntnisse in der Programmiersprache C sind zum Verständnis unabdingbar. Vor allem der Umgang mit Pointern und Funktionsadressen sollte vertraut sein.

Zusätzliche Informationsquellen

Errata und vor allem auch den Code zu den im Buch vorgestellten Beispieltreibern finden Sie unter http://ezs.kr.hsnr.de/TreiberBuch/. Die sicherlich wichtigste Informationsquelle zur Erstellung von Gerätetreibern ist der Quellcode des Linux-Kernels selbst. Wer nicht mit Hilfe der Programme find und grep den Quellcode durchsuchen möchte, kann auf die »Linux Cross-Reference« (http://lxr.linux.no) zurückgreifen. Per Webinterface kann der Quellcode angesehen, aber auch nach Variablen und Funktionen durchsucht werden.

In den Kernelquellen sind zudem so genannte Skeleton-Treiber zu finden. Darunter verstehen die Entwickler ein Treibergerüst für ein spezifisches Subsystem. Dieses Treibergerüst kann als Grundlage für den eigenen Treiber eingesetzt werden. Allerdings sind die Skeleton-Treiber nicht immer aktuell. Folgende gibt es: drivers/net/pci-skeleton.c, drivers/net/isa-skeleton.c, drivers/video/skeletonfb.c, drivers/pci/hotplug/pcihp_skeleton.c und drivers/usb/usb-skeleton.c.

In den Kernel-Quellen befindet sich eine sehr hilfreiche Dokumentation. Ein Teil der Dokumentation besteht aus Textdateien, die sich mit jedem Editor ansehen lassen. Ein anderer Teil der Dokumentation muss erst erzeugt werden. Dazu wird im Hauptverzeichnis der Kernelquellen (/usr/src/linux/) eines der folgenden Kommandos aufgerufen:

(root)# make psdocs    # für Dokumentation in Postscript
(root)# make pdfdocs   # für Dokumentation in PDF
(root)# make htmldocs  # für HTML-Dokumentation

Sind die notwendigen DocBook-Pakete installiert (unter Debian unter anderem das Paket docbook-utils), werden eine Reihe unterschiedlicher Dokumente generiert und in das Verzeichnis /usr/src/linux/Documentation/DocBook/ abgelegt. Insbesondere sind hier die folgenden Dokumente zu finden:

kernel-api

Dieses Dokument enthält die Beschreibung einiger Funktionen des Betriebssystemkerns.

kernel-hacking

Kernelentwickler Rusty Russell führt in einige Grundlagen der Kernelentwicklung ein. Leider ist das Dokument nicht mehr ganz aktuell.

kernel-locking

Rusty Russell »Unreliable Guide to kernel-locking«. Hier finden sich einige Aspekte wieder, die die Vermeidung beziehungsweise den Schutz kritischer Abschnitte betreffen.

writing_usb_driver

Eine Einführung in die Erstellung von USB-Hosttreibern.

gadget

Eine Einführung in die Erstellung von USB-Slavetreibern.

parportbook

Eine Einführung in die Erstellung von Treibern, die auf die parallele Schnittstelle über das Parport-Subsystem von Linux zugreifen.

Neben der Dokumentation, die den Kernelquellen beiliegt, gibt es noch diverse Informationsquellen auf dem Internet:

http://www.kernelnewbies.org

Hier finden sich viele Einsteigerinformationen und Programmiertricks.

http://www.kerneltrap.org

Aktuelle Informationen und eine Zusammenfassung der Diskussionen auf der Kernel-Mailingliste präsentiert diese Webseite.

http://www.lwn.net

Immer donnerstags gibt es hier aktuelle Kernelnews sowie Tipps und Tricks rund um die Kernel- und Treiberprogrammierung. Die ganz aktuelle Ausgabe steht jeweils nur der zahlenden Klientel zur Verfügung. Wer ohne Obulus auskommen will, kann die jeweils vorherige Ausgabe kostenlos lesen.

http://www.kernel.org

Der Server »kernel.org« ist die zentrale Stelle für aktuelle und auch für alte Kernelversionen. Darüber hinaus finden sich hier die Patches einiger Kernelentwickler.

http://www.lkml.org

Hier lässt sich die Kernel- Mailing-Liste aktuell mitlesen, ohne selbst eingeschrieben sein zu müssen.

Zu jeweiligen Spezialgebieten der Treiberentwicklung gibt es im Internet überdies einige Texte oder Artikel. Hier ist der Leser allerdings selbst gefordert, mit Hilfe einer Suchmaschine Zusatzmaterial zu finden.

Bücher zum Thema Treiberentwicklung sind dünn gesät, insbesondere zum Kernel 2.6. Zum Kernel 2.4 gibt es das Buch von Rubini und Corbet:

Zum Thema Linux-Kernel im Allgemeinen gibt es für Kernel 2.6 das Buch:

Wenn es um den Kernel 2.2 und 2.4 geht, ist die Auswahl der Bücher über den Kernel im Allgemeinen umfangreicher. Stellvertretend sei hier das Buch von Bovet et al. genannt:

Zu guter Letzt bleibt noch der Verweis auf unsere Artikelserie im Linux-Magazin ab Ausgabe 8/2003, die das Thema Kernelprogrammierung für den Kernel 2.6 behandelt.


Lizenz