Open Source im professionellen Einsatz

Kernel- und Treiberprogrammierung mit dem künftigen Kernel 2.6 - Folge 8

Kern-Technik

,

Die Geräteklasse der Blockdevices bildet die Grundlage für Filesysteme. Diese Kern-Technik-Folge erklärt die Treiberkomponenten und zeigt die Implementierung einer RAM-Disk als Beispiel für das neue Blockgeräte-Interface in Kernel 2.6.

Der Name klingt zwar sperrig, doch Blockgeräte haben mit großen Kisten nichts zu tun. Festplatten, DVDs, CD-ROMs, USB-Sticks oder Flashcards verarbeiten Daten blockweise und verlangen deshalb nach einem Blockgeräte-Interface im Kernel. Meist dienen sie dazu, Daten dauerhaft zu speichern, also als Hintergrundspeicher, dessen Verkörperung eben eine Festplatte ist. Programme können mit Funktionen wie »fseek()« oder »lseek()« an beliebige Stellen auf dem Blockgerät springen und dort lesen oder schreiben (Random Access).

Am Ende anfangen

Dabei lässt sich das letzte Byte also durchaus vor dem ersten lesen. Vergleicht man den Transport von Daten mit fließendem Wasser, handelt es sich bei einem Blockgerät nicht um einen Datenstrom, sondern eher um ein stehendes Gewässer. Wer hier Wasser schöpfen will, kann dazu nur einen Eimer benutzen - selbst dann, wenn er lediglich einen einzigen Wassertropfen benötigt. Beim Kernel 2.6 fasst ein solcher Eimer genau 512 Bytes. Wer von einem Blockgerät ein einzelnes Byte lesen möchte, muss also stets 512 Bytes entnehmen. Die restlichen 511 Bytes lässt er dann einfach liegen.

Da von einem Blockgerät ohnehin keine einzelnen Bytes gelesen werden können, verwaltet der Kernel den Hintergrundspeicher über Blocknummern, unabhängig davon, dass auf Hardware-Ebene Köpfe, Zylinder und Sektoren dahinter stecken. Für die ersten 512 Bytes eines Hintergrundspeichers ist Blocknummer 0 maßgeblich, Bytes 512 bis 1023 entsprechen Blocknummer 1 und so weiter.

Ist von einem Blockgerät ein Byte zu lesen, berechnet der Kernel zunächst, in welchem Block sich das Byte befindet, und kopiert diesen Block über seine Nummer in den Hauptspeicher. Dann liest er aus der Kopie im Speicher das gewünschte Byte. Lautet der Auftrag, ein Byte zu schreiben, liest er den entsprechende Block ebenfalls in den Hauptspeicher ein (siehe Abbildung 1).

Abbildung 1: Der Kernel verwendet für den Zugriff auf Daten eines Blockgeräts Kopien der Blöcke im Hauptspeicher. Dafür stellt ein Treiber die Funktion »request_fn()« bereit.

Abbildung 1: Der Kernel verwendet für den Zugriff auf Daten eines Blockgeräts Kopien der Blöcke im Hauptspeicher. Dafür stellt ein Treiber die Funktion »request_fn()« bereit.

Nach der Änderung der Kopie schreibt er den Block auf das Gerät zurück. Allerdings nicht unbedingt sofort: Aus Performancegründen wartet das System vor dem physischen Zurückschreiben erst einmal ab, ob noch weitere Schreibaufträge für den Block eintreffen.

Namen statt Zahlen

Damit der Anwender Daten auf dem Blockgerät ablegen und wiederfinden kann, verwaltet der Kernel die Blöcke eines solchen Geräts über ein Dateisystem, beispielsweise Ext 3 (siehe auch Titelthema-Artikel). Durch selbst vergebene Namen für Verzeichnisse und Dateien ist der Zugriff für den Anwender einfacher, als wenn er Blocknummern verwenden müsste.

Auch dem Treiberprogrammierer bringt die Abstraktion Vorteile. Er muss sich nicht darum kümmern, was eine Anwendung auf der Ebene des Dateisystems macht. Sein Treiber bekommt stattdessen vom Betriebssystem so simple Aufträge wie "Kopiere den Inhalt von Block xyz in die Mempage abc" oder "Schreibe den Inhalt von Mempage abc in Block xyz".

Um vernünftige Aufträge zu erteilen, braucht der Kernel einige Informationen über den Hintergrundspeicher, etwa dessen Größe angegeben als Anzahl der Blöcke. Außerdem muss er die Major- und Minor-Nummer kennen, um mit ihrer Hilfe über die Gerätedatei die Zuordnung zwischen Applikation respektive Dateisystem und Treiber herzustellen (siehe Kasten "Gerätenummern").

Gerätenummern

Mit Kernel 2.6 hat Linus Torvalds das Ende der klassischen Major- und Minor-Nummern eingeläutet. Anders als geplant sind die neuen Gerätenummern aber nicht 64, sondern nur 32 Bit breit. Abbildung 5 zeigt, wie die alten Major- und Minor-Nummern auf die neuen Gerätenummern abgebildet werden. Das im Artikel vorgestellte Treiberinterface arbeitet weiterhin nach dem alten Schema.

Wer die neuen Gerätenummern nutzen möchte, muss auf zusätzliche Funktionen zurückgreifen. Dazu meldet der Treiber sein Interesse an den benötigten Gerätenummern durch den Aufruf der Funktion »blk_register _region()« an:

void blk_register_region(
   dev_t dev,
   unsigned long range,
   struct module *module,
   struct kobject *(*probe)(dev_t,int *,void *),
   int (*lock)(dev_t,void *),
   void *data);


Allerdings sind damit die Gerätenummern nicht sofort exklusiv reserviert. Vielmehr informiert der Kernel den Treiber, sobald er auf eine Disk im entsprechenden Bereich zugreifen möchte, durch Aufruf von »probe()«. Diese Funktion sucht die passende »struct gendisk« und gibt einen Zeiger auf das in der Struktur eingebettete »kobject« zurück. Hat der Programmierer außerdem die Adresse einer Funktion »lock()« übergeben, ruft der Kernel diese zuvor auf.

Der Datentyp »dev_t« repräsentiert die neue Gerätenummer. Das Makro »MK_DEV(major,minor)« generiert aus der Major-Nummer »major« und der Minor-Nummer »minor« die Gerätenummer. Der Parameter »range« von »blk_register_region()« gibt die Anzahl der zu reservierenden Gerätenummern an.

Wird der Treiber deinitialisiert, nimmt er durch Aufruf von »blk_unregister_region(dev_t dev, unsigned long range)« die Reservierung wieder zurück (siehe auch[4]).

Abbildung 5: Eine eindeutige Vorschrift bildet die alten Major- und Minor-Nummern auf die neuen Gerätenummern ab. »MKDEV()« erzeugt aus Major/Minor eine Gerätenummer, die Makros »MAJOR()« und »MINOR()« sind für die Gegenrichtung zuständig.

Abbildung 5: Eine eindeutige Vorschrift bildet die alten Major- und Minor-Nummern auf die neuen Gerätenummern ab. »MKDEV()« erzeugt aus Major/Minor eine Gerätenummer, die Makros »MAJOR()« und »MINOR()« sind für die Gegenrichtung zuständig.

Diesen Artikel als PDF kaufen

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook