Treiberfunktionen können aus mehreren Gründen nicht direkt auf den Speicherbereich einer zugehörigen Treiberinstanz zugreifen. Zum einen findet durch die Memory Management Unit eine Adressumsetzung statt, und die Adresslage der Applikationsspeicherbereiche ist dem Treiber nicht bekannt. Zum anderen können die Speicherbereiche im Kernel- und User-Space vor einem gegenseitigen Zugriff geschützt sein.
Für die Treiberentwicklung ergibt sich daraus die Konsequenz, dass der Zugriff auf Speicherbereiche der Applikation nicht ohne Mithilfe des Betriebssystemkerns möglich ist. Zu diesem Zweck stellt der Kernel Funktionen zur Verfügung, mit denen Daten zwischen Kernel und Applikation ausgetauscht werden können:
Die Funktionen sind in der Header-Datei <asm/uaccess.h> deklariert.
Bevor diese Funktionen die Daten kopieren, stellen sie sicher, dass die zugehörigen Speicherbereiche wirklich vorhanden sind und dass auf diese zugegriffen werden kann. Der Kernel kann schließlich nicht davon ausgehen, dass ihm die Applikation in jedem Fall eine korrekte Adresse übergibt. Der Zugriff auf eine inkorrekte Adresse könnte zu Instabilitäten führen, was in jedem Fall zu vermeiden ist.
Ruft eine Applikation einen Systemcall auf, der einen Datentransfer bewirken soll (z.B. read), wird dem zugehörigen Treiber die in der Applikation verwendete (logische) Adresse 1:1 übergeben. Die durch die Applikation übergebenen Adressinformationen werden in den erwähnten Funktionen (copy_to_user, copy_from_user, ...) direkt als Parameter verwendet. Zu beachten ist jedoch, dass der Zugriff nur auf Speicherbereiche des gerade aktiven Rechenprozesses möglich ist.
Die Funktionen copy_to_user und copy_from_user geben die Anzahl der Bytes zurück, die nicht kopiert werden konnten. Im Normalfall wird »0« zurückgegeben als Zeichen dafür, dass die Kopieraktion erfolgreich war (siehe Beispiel Datentransfer zwischen Kernel- und User-Space).
Beispiel 5-16. Datentransfer zwischen Kernel- und User-Space
Die Funktionen copy_to_user und copy_from_user kopieren Speicherbereiche. Daneben gibt es die Makros, die jeweils einen einzelnen Wert, der ein Byte, zwei Byte, 4 Byte oder 8 Byte lang sein kann, kopieren: put_user und get_user. Das Makro put_user bekommt als ersten Parameter den Wert und als zweiten Parameter die Adresse übergeben, an die der Wert abgelegt werden soll (siehe Datentransfer mit Hilfe der Funktion put_user). Muss nur ein einzelner Wert kopiert werden, wird man aus Performancegründen zu diesen Makros greifen.
get_user kopiert einen Wert aus dem Speicherbereich der Applikation in den Speicherbereich des Kernels. Der erste Parameter ist die Variable, in die der Wert kopiert wird. Der zweite Parameter spezifiziert die Adresse, woher der Wert stammt. Wieviel Bytes (1, 2, 4 oder 8) jeweils kopiert werden sollen, wird im Makro aufgrund des Datentyps des zweiten Parameters bestimmt.
Beispiel 5-17. Datentransfer mit Hilfe der Funktion put_user
Die bisher zum Datentransfer vorgestellten Funktionen überprüfen den durch die Applikation übergebenen Pointer, ob dieser auf einen gültigen Speicherbereich zeigt. Sobald innerhalb eines durch die Applikation aufgerufenen Systemcalls (z.B. ioctl) mehrfach Daten zwischen Kernel und Applikation kopiert werden, steigt der Aufwand für den Entwickler: Mit jedem Aufruf wird die Gültigkeit respektive die Existenz eines Speicherbereiches überprüft. Zur Optimierung bietet Linux daher die Möglichkeit, das Überprüfen des Speicherbereiches vom Kopieren der Daten zu splitten. Die Funktionen, über die der Treiberentwickler direkt zugreifen kann, lauten:
Ob ein Speicherbereich gültig ist oder nicht, kann explizit über die Funktion access_ok festgestellt werden. Als Parameter sind der Pointer, die Größe des zu überprüfenden Bereiches und die Art des Zugriffes (lesen oder schreiben) zu übergeben. Beispiel Optimierter Datentransfer zeigt die Anwendung der Funktion.
Beispiel 5-18. Optimierter Datentransfer
Zurück | Zum Anfang | Weiter |
Die Treiber-Einsprungspunkte | Nach oben | Hardware anbinden |