Schnittstellenspezifische Typen

Für die am häufigsten benutzten Datentypen im Kernel gibt es eigene typedef-Anweisungen, um Portabilitätsprobleme zu vermeiden. Beispielsweise ist die Prozeß-ID (PID) normalerweise ein pid_t anstelle eines int. Die Verwendung von pid_t versteckt mögliche Unterschiede in den tatsächlichen Datentypen. Wir verwenden den Ausdruck schnittstellenspezifisch für einen Typ, der von einer Bibliothek definiert wird, um eine Schnittstelle zu einer spezifischen Datenstruktur bereitzustellen.

Selbst wenn kein schnittstellenspezifischer Typ definiert ist, ist es immer wichtig, den passenden Datentyp auf eine Weise zu verwenden, die mit dem Rest des Kernels konsistent ist. Ein Jiffy-Zähler ist immer unsigned long, unabhängig von seiner tatsächlichen Größe. Aber möchten Sie immer jiffy_t schreiben? Wir konzentrieren uns hier auf die erste Klasse der schnittstellenspezifischen Typen: auf diejenigen, deren Namen auf _t enden.

Die vollständige Liste aller _t-Typen steht in <linux/types.h>, nützt einem aber selten. Wenn Sie einen bestimmten Typ benötigen, finden Sie ihn im Prototyp der Funktionen, die Sie aufrufen müssen, oder in den Datenstrukturen, die Sie verwenden.

Wenn Ihr Treiber Funktionen verwendet, die solche “speziellen” Typen erwarten und der Konvention nicht folgen, wird der Compiler eine Warnung melden. Wenn Sie den Compiler-Schalter –Wall verwenden und sorgfältig alle Warnungen beseitigen, können Sie ziemlich sicher sein, daß Ihr Code portabel ist.

Das Hauptproblem bei den _t-Datenelementen ist, daß Sie sie nicht so einfach ausgeben können, weil es nicht so leicht ist, das richtige Ausgabeformat für printk oder printf zu finden; Warnungen, die Sie auf einer Architektur beseitigt haben, treten auf einer anderen wieder auf. Wie wollen Sie beispielsweise einen Wert des Typs size_t ausgeben, wo size_t auf manchen Plattformen unsigned long, auf anderen aber unsigned int ist?

Wenn Sie schnittstellenspezifische Daten ausgeben wollen, ist es am besten, wenn Sie den Wert per Cast-Operation auf den größtmöglichen Typ (normalerweise long oder unsigned long) umwandeln und dann mit dem entsprechenden Format ausgeben. Damit werden keine Warnungen oder Fehler gemeldet, weil das Format zum Typ paßt, und Sie verlieren auch keine Datenbits, weil die Cast-Operation entweder keine Wirkung hat oder das Element zu einem größeren Datentyp erweitert.

In der Praxis sind die Datentypen, über die wir hier reden, aber gar nicht dazu gedacht, ausgegeben zu werden, so daß dieses Problem ohnehin nur bei Debugging-Meldungen auftritt. Meistens muß der Code die Interface-spezifischen Typen nur speichern und vergleichen, sowie sie als Argumente von Bibliotheks- oder Kernel-Funktionen weiterreichen.

Obwohl die _t-Typen in den meisten Fällen die richtige Lösung sind, gibt es manchmal nicht den richtigen Typ. Das ist zum Beispiel bei alten Schnittstellen der Fall, die noch nicht aufgeräumt worden sind.

Wir haben in den Header-Dateien des Kernels eine einzige mehrdeutige Stelle gefunden, und zwar in den nur lose definierten Datentypen von I/O-Funktionen (siehe dazu den Abschnitt “the Section called Plattformabhängigkeiten in Kapitel 8” in Kapitel 8). Das liegt hauptsächlich an historischen Gründen, kann aber beim Programmieren zu Problemen führen. Beispielsweise kann man in Schwierigkeiten geraten, wenn man die Argumente von Funktionen wie outb vertauscht; wenn es einen Typ port_b gäbe, würde der Compiler solche Fehler finden.