Workshop: Kernel- und Treiberprogrammierung mit dem Kernel 2.6
- Folge 29
Kern-Technik
von Eva-Katharina Kunst, Jürgen Quade
Erschienen im Linux-Magazin
2006/09
Das Relay-Subsystem ermöglicht den Datenaustausch großer Datenmengen zwischen Kernel- und Userspace in Hochgeschwindigkeit. Diese Kern-Technik-Folge erklärt dies Schritt für Schritt.
Debug- oder Log-Informationen werden innerhalb des Kernels klassischerweise mit Hilfe von »printk()« in den Userspace übertragen und dort vom Syslog weiterverarbeitet. Neben dieser einfachen Variante hat sich in der Vergangenheit das Proc- und seit neuestem das Debug-Filesystem etabliert. Ob per »printk()« oder mit der Ausgabe über ein virtuelles Dateisystem: Der Transfer großer Datenmengen zwischen Kernel und Applikationen ist aufwändig und zudem programmiertechnisch kompliziert. Daten werden nämlich zuerst in Puffern (die überlaufen können) zwischengespeichert, bevor sie im Speicherbereich einer Applikation landen.
Um diese Probleme zu lösen, entwickelten einige Kernelhacker mit dem Relay-FS ein neues, virtuelles Dateisystem, das einerseits einfach zu handhaben ist und andererseits dank »mmap()« einer Anwendung den direkten Zugriff auf die Daten ohne weiteren Kopiervorgang ermöglicht. Mit dem Kernel 2.6.14 nahm Linus Torvalds dieses Dateisystem in den Kernel auf.
Suchen Sie allerdings in einem aktuellen Kernel nach dem Relay-Filesystem, wird die Suche vergeblich sein: Getreu dem Motto "Keep it simple" haben die Entwickler den Code kräftig abgespeckt, einige Betriebsmodi wie Lockless und den Part des virtuellen Dateisystems komplett entfernt und nur das Kernfeature übrig gelassen: schnelle Transfers großer Datenmengen zwischen Kernel- und Userspace. Statt eines speziellen Filesystems sollen die Nutzer jetzt die vorhandenen virtuellen Dateisysteme nutzen, insbesondere das Debug-Filesystem. An einer einfachen Anbindung des Sys-FS arbeiten die Entwickler noch.
Debug-Filesystem hilft
Wollen Sie also von einem Treiber oder einem anderen Kernelmodul aus einer Anwendung Daten zur Verfügung stellen, bietet sich eine Relay-Datei im Debug-Filesystem an. Das Verfahren ist denkbar einfach (siehe Abbildung 1): Als Erstes legen Sie mit »debugfs_create_dir()« im Debug-Filesystem ein Verzeichnis an und erzeugen dann einen so genannten Relay-Kanal (»rchan«, »relay_open()«, siehe Tabelle 1).

|
Abbildung 1: In vier Schritten zum eigenen Relay-Kanal. Dann ist er mit der Funktion »relay_write()« verwendbar.
|
Dafür ist es nötig, zwei kurze Callback-Funktionen zu implementieren (siehe Abbildung 2). Den ersten Callback ruft der Kernel auf, wenn das Relay-Subsystem die Relay-Datei anlegen möchte. Erzeugen Sie dazu die Datei mit Hilfe des Debug-Filesystems (siehe Kasten "Debug-Filesystem"). Der zweite Callback wird aufgerufen, um die Relay-Datei wieder zu entfernen. Damit löschen Sie die zuvor erzeugte Datei im Debug-Filesystem wieder.

|
Abbildung 2: Bei den zu implementierenden Callbacks für eine Relay-Datei sind optionale und obligatorische zu unterscheiden.
|
|
Mit dem Debug-Filesystem haben die Kernelentwickler eine elegante Möglichkeit geschaffen, einzelne Variablen und ganze Speicherbereiche (Blobs) des Kernels einer Applikation zum Lesen und auch zum Schreiben zur Verfügung zu stellen. Die Variablen selbst sind aus Sicht der Anwendung auf Pseudodateien abgebildet, die der Anwendungsentwickler auch über die Systemcalls »read()« und »write()« lesen und schreiben kann. Beim Lesen wird der Wert der Variablen in eine Ascii-Repräsentation umgewandelt. Entsprechend übergibt die Anwendung den Wert ebenfalls in Ascii.
Die Programmierschnittstelle zu dem Debug-Filesystem ist einfach, Tabelle 2 listet die Prototypen der Funktionen auf. Angenehm ist: Jeweils nur ein Funktionsaufruf legt sowohl ein Verzeichnis im Debug-Filesystem als auch eine Variablendatei an. Der Funktion zum Erstellen der Variablendatei übergeben Sie unter anderem die Adresse der Variablen, die beim Zugriff auf die Datei ausgelesen respektive geschrieben werden soll. Als Datentypen stehen neben den Standardformaten (»u8«: Unsigned Char, »u16«: Unsigned Short, »u32« Unsigned Int) auch »boolean« und »blob« zur Verfügung. Bei Blobs handelt es sich um normale Speicherbereiche zum Austausch binärer Daten.
Pseudodateien selbst gemacht
Zusätzlich erlaubt das Debug-FS das Anlegen eigener Pseudodateien. Dabei implementieren Sie selbst die Lese- und die Schreibfunktion »read()« und »write()«. Diesen Mechanismus nutzt auch das Relay-Subsystem. Das Aufräumen übernimmt »debugfs_remove()«.
Listing 1 zeigt ausschnittweise den Einsatz des Debug-Filesystems an einem einfachen Beispiel, der vollständige Code ist auf der Website des Linux-Magazins zu finden [3]. Zeile 7 erzeugt zunächst ein Verzeichnis, Zeile 12 legt innerhalb des neuen Verzeichnisses die Debug-FS-Datei »index« an. Eine Applikation kann über diese Datei die Variable »index« lesen und schreiben. Der Datenaustausch zwischen User- und Kernelspace findet ebenfalls im Ascii-Format statt.
Und noch zwei Hinweise. Erstens: Bei den über das Debug-Filesystem exportierten Variablen muss es sich um globale Variablen handeln. Zweitens: Auf Blobs lässt sich nur lesend zugreifen. Überhaupt ist der Zugriff auf die Variablen natürlich nur dann möglich, wenn das Debug-Filesystem gemountet ist.
|
01 ...
02 static struct dentry *vardir, *varfile;
03 static u32 index=99;
04
05 static int __init mod_init(void)
06 {
07 vardir = debugfs_create_dir("vardir", NULL);
08 if( vardir==NULL ) {
09 printk("Kann debugfs-dir nicht anlegenn" );
10 return -EIO;
11 }
12 varfile = debugfs_create_u32("index", S_IRUGO|S_IWUGO, vardir, &index);
13 if( varfile==NULL ) {
14 printk("Kann debugfs-file nicht erzeugenn");
15 debugfs_remove( vardir );
16 return -EIO;
17 }
18 printk("Laden: index hat den Wert: %dn", index );
19 return 0;
20 }
21
22 static void __exit mod_exit(void)
22 {
23 printk("Entladen: index hat den Wert: %dn", index );
24 debugfs_remove( varfile );
25 debugfs_remove( vardir );
26 }
27 ...
|
Mehrere CPUs
Die über den so angelegten Relay-Kanal publizierten Informationen bildet der Userspace auf einen Dateizugriff ab, wobei für jede CPU eine eigene Ausgabedatei existiert. Auf einer Einprozessor-Maschine gibt es also eine, auf einer Zweiprozessor-Maschine zwei Relay-Dateien. Hintergrund dieser Maßnahme: Jede CPU schreibt die Daten in eigene Per-CPU-Variablen (Ringpuffer), Locking ist nicht notwendig (siehe [1]). Der für jede CPU einmal vorhandene Ringpuffer unterteilt sich wiederum in Teilpuffer (siehe Abbildung 3).

|
Abbildung 3: Kernel-intern ist die Relay-Datei als Ringpuffer realisiert, der seinerseits aus mehreren Teilpuffern besteht.
|
Größe und Anzahl dieser Puffer werden beim Erzeugen des Kanals festgelegt. Die Gesamtgröße des verwendeten Speichers ergibt sich über die Formel: Gesamtgröße = (Anzahl_Puffer * Puffergröße) * Anzahl_Prozessoren.
Mehrere Kriterien bestimmen die Größe der einzelnen Puffer. Zum einen sollte sie ein Vielfaches von 2 sein und am besten in Beziehung zu einer Page (4096 Bytes) stehen. Das Relay-Subsystem reserviert den notwendigen Speicher nämlich auf Basis von Speicherseiten. Zum anderen sollte die längste, durch einen Funktionsaufruf zu schreibende Nachricht in den Puffer passen. Denn das Relay-Subsystem weigert sich aus Performance-Gründen, Log-Messages auf mehrere Puffer aufzusplitten. Wählen Sie beispielsweise eine Puffergröße von 128 Bytes, können Sie nicht mit einem einzelnen Aufruf 129 Bytes schreiben, die Daten würden verworfen.
Sind die zu schreibenden Häppchen klein genug, werden sie hintereinander in den aktiven Puffer geschrieben, solange noch ausreichend Platz ist, um die Nachricht komplett abzulegen. Sollte der Puffer bereits so weit gefüllt sein, dass die Daten des aktuellen Auftrags nicht mehr hineinpassen, gilt der Puffer als gefüllt, auch wenn einige Bytes ungenutzt bleiben, die man Padding-Bytes nennt. Die Daten des aktuellen Auftrags fließen dann in den nächsten Puffer.
Sobald der letzte Puffer gefüllt ist, legt das Relay-Subsystem die Daten wieder im ersten Puffer ab. Ob es tatsächlich dazu kommt, hängt allerdings davon ab, ob eine Applikation die Daten in der Zwischenzeit gelesen hat. Ist dies nicht der Fall, werden die neuen Daten verworfen. Zur Realisierung zählt das Relay-Subsystem beim Aufruf von »read()« mit, wie viele Daten bereits in den Userspace transferiert wurden.
|
Ähnliche Artikel
|
|
Kern-Technik
|
Kernel- und Treiberprogrammierung mit dem Kernel 2.6 -
Folge 49
|
|
Kern-Technik
|
Kernel- und Treiberprogrammierung mit dem Kernel 2.6 -
Folge 27
|
|
Kern-Technik
|
Kernel- und Treiberprogrammierung mit dem Kernel 2.6 - Folge 44
|
|
Kern-Technik
|
Kernel- und Treiberprogrammierung mit dem Kernel 2.6 - Folge 51
|
|
Kern-Technik
|
Kernel- und Treiberprogrammierung mit dem Kernel 2.6 - Folge
18
|
|
Kern-Technik
|
Kernel- und Treiberprogrammierung mit dem Kernel 2.6 - Folge 35
Folge 35
|
| Whitepaper |
|
Usage Landscape Enterprise Open Source Data Integration
Die Nachfrage nach Datenintegrationslösungen für Unternehmen ist zunehmend gestiegen und vor allem das Interesse an Open Source Technologien wird immer größer. Doch wie und von wem werden Open Source Datenintegrationslösungen genutzt und welches Nutzungsverhalten lässt sich daraus ableiten? Das vorliegende White Paper präsentiert die Erfahrungswerte von über 1000 Open Source Nutzern und liefert fundierte Antworten auf diese Fragen.
Download PDF (Registrierung erforderlich)
|
|
Daten Migration - Eine Publikation von Bloor Research
Datenmigrationsprojekte überschreiten häufig das Budget, neigen zu Verzögerung und werden unter Umständen komplett abgebrochen. Bloor Research ist eines der weltweit führenden IT-Forschungs-, Analyse- und Beratungsunternehmen und wird in dem vorliegenden White Paper die wichtigsten Aspekte dieser Problematik näher beleuchten. Ferner werden praktische Empfehlungen für erfolgreiche Migrationsprojekte gegeben, die Sie auf Ihr nächstes Projekt übertragen können.
Download PDF (Registrierung erforderlich)
|
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links"
nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedrucken Fassung entsprechen.
|