Datenelementen eine explizite Größe zuweisen

Manchmal ist es im Kernel-Code notwendig, daß Datenelemente eine spezifische Größe haben, entweder um zu vordefinierten binären Strukturen zu passen[1] oder um Daten in Strukturen durch Einfügen von “Füllfeldern” auszurichten (aber lesen Sie auch “Datenausrichtung” weiter hinten in diesem Kapitel, um Informationen über Probleme der Ausrichtung zu bekommen).

Der Kernel stellt zu diesem Zweck die folgenden Datentypen bereit, die Sie verwenden können, wenn Sie wissen müssen, wie groß Ihre Daten sind. Alle diese Typen werden in <asm/types.h> deklariert, was wiederum von <linux/types.h> eingebunden wird:


u8;   /* vorzeichenloses Byte (8 Bits) */
u16;  /* vorzeichenloses Wort (16 Bits) */
u32;  /* vorzeichenloser 32-Bit-Wert */
u64;  /* vorzeichenloser 64-Bit-Wert */

Diese Datentypen stehen nur für Kernel-Code zur Verfügung (d.h. __KERNEL__ muß definiert werden, bevor <linux/types.h> eingebunden wird). Es gibt auch die zugehörigen vorzeichenbehafteten Typen, die aber selten benötigt werden. Ersetzen Sie einfach das u im Namen durch s, wenn Sie doch einen benötigen.

Wenn ein Programm im User-Space diese Typen benötigt, kann es ihren Namen einen doppelten Unterstrich voranstellen: __u8 und die anderen Typen sind unabhängig von __KERNEL__ definiert. Wenn ein Treiber beispielsweise binäre Strukturen mit einem Programm im User-Space über ioctl austauschen muß, dann sollten die Header-Dateien die 32-Bit-Felder in den Strukturen als __u32 deklarieren.

Man darf nicht vergessen, daß diese Typen Linux-spezifisch sind; ihre Verwendung steht einer Portierung auf andere Unix-Systeme im Wege. Systeme mit neueren Compilern unterstützen die C99-Standardtypen wie uint8_t und uint32_t; nach Möglichkeit sollten diese Typen anstelle der Linux-spezifischen Varianten verwendet werden. Wenn Ihr Code aber auch mit 2.0-Kerneln funktionieren muß, dann ist die Verwendung dieser Typen nicht möglich (nur ältere Compiler funktionieren mit 2.0-Kerneln).

Vielleicht ist Ihnen aufgefallen, daß im Kernel manchmal auch konventionelle Typen wie unsigned int verwendet werden, wenn die Größe der Elemente architekturunabhängig ist. Das wird hauptsächlich wegen der Rückwärtskompatibilität gemacht. Als u32 und verwandte Typen in Version 1.1.67 eingeführt wurden, konnten die Entwickler die existierenden Datenstrukturen nicht verändern, weil der Compiler jedesmal eine Warnung ausgibt, wenn es eine Nichtübereinstimmung zwischen dem Feld einer Struktur und dem zugewiesenen Wert gibt.[2] Linus hat nicht damit gerechnet, daß das Betriebssystem, das er einmal für sich selbst geschrieben hatte, auf andere Plattformen portiert werden würde; deswegen haben ältere Strukturen manchmal nur schwache Typen.

Fußnoten

[1]

Das passiert zum Beispiel beim Lesen von Partitionstabellen, beim Ausführen von Binärdateien oder beim Decodieren eines Netzwerk-Paketes.

[2]

Der Compiler meldet sogar Typen-Nichtübereinstimmungen, wenn zwei Typen nur verschiedene Namen für ein- und dasselbe Objekt sind, wie beispielsweise unsigned long und u32 auf PCs.