Als wir über Zeichen-Treiber gesprochen haben, haben wir die letzten beiden Datei-Operationen in der block_device_operations-Struktur ignoriert, weil sie nur für herausnehmbare Block-Geräte existieren. Jetzt ist es an der Zeit, sich damit zu beschäftigen. sbull ist eigentlich kein herausnehmbares Gerät, tut aber so und implementiert daher diese Methoden.
Die Operationen, über die wir hier reden, sind check_media_change und revalidate. Erstere wird verwendet, um herauszufinden, ob sich das Gerät seit dem letzten Zugriff geändert hat, letztere reinitialisiert den Zustand des Geräts nach einem Wechsel.
Bei sbull wird der Datenbereich des Gerätes eine halbe Minute, nachdem der Verwendungszähler auf 0 gefallen ist, freigegeben. Wenn das Gerät lange genug ausgehängt (oder geschlossen) ist, dann reicht das als Simulation eines Diskettenwechsels, und der nächste Zugriff auf das Gerät alloziert einen neuen Speicherbereich.
Diese “verzögerte Freigabe” wird mit einem Kernel-Timer implementiert.
Diese Funktion hat als einziges Argument ein kdev_t, das das Gerät identifiziert. Der Rückgabewert ist 1, wenn das Medium gewechselt wurde, und 0 sonst. Ein Block-Treiber, der keine herausnehmbaren Geräte unterstützt, muß diese Funktion nicht implementieren, wenn er fops->check_media_change auf NULL setzt.
Wenn es sich um ein Gerät mit herausnehmbaren Medien handelt, aber nicht festgestellt werden kann, ob das Medium gewechselt wurde, dann ist es immer eine sichere Sache, 1 zurückzugeben. Das ist das Verhalten der IDE-Treiber, wenn sie es mit herausnehmbaren Medien zu tun haben.
Die Implementation in sbull gibt 1 zurück, wenn das Gerät durch den Ablauf des Timers bereits aus dem Speicher entfernt worden ist, und 0, wenn die Daten noch gültig sind. Wenn das Debugging eingeschaltet ist, wird auch eine Nachricht in die Systemprotokolle ausgegeben; der Benutzer kann also feststellen, wann die Methode vom Kernel aufgerufen worden ist.
intsbull_check_change(kdev_ti_rdev) { intminor=MINOR(i_rdev); Sbull_Dev*dev=sbull_devices+minor; PDEBUG("check_changefordev%i\n",minor); if(dev->data) return0;/*nochgueltig*/ return1;/*nichtmehrgueltig*/ } |
Die Überprüfungsfunktion wird aufgerufen, wenn ein Medienwechsel festgestellt wurde. Außerdem wird sie auch von den diversen stat-Systemaufrufen der Kernel-Version 2.1 aufgerufen. Der Rückgabewert wird derzeit nicht benutzt. Um sicherzugehen, sollte 0 im Erfolgsfall und ein negativer Wert im Fehlerfall zurückgegeben werden.
Die von revalidate durchgeführte Operation ist gerätespezifisch, aber normalerweise aktualisiert die Funktion die interne Statusinformation anhand des neuen Mediums.
In sbull versucht revalidate einen neuen Datenbereich zu allozieren, wenn es noch keinen gibt.
intsbull_revalidate(kdev_ti_rdev) { Sbull_Dev*dev=sbull_devices+MINOR(i_rdev); PDEBUG("revalidatefordev%i\n",MINOR(i_rdev)); if(dev->data) return0; dev->data=vmalloc(dev->size); if(!dev->data) return-ENOMEM; return0; } |
Treiber für herausnehmbare Geräte sollten auch überprüfen, ob es einen Medienwechsel gegeben hat, wenn das Gerät geöffnet wird. Der Kernel stellt dafür eine Funktion bereit:
intcheck_disk_change(kdev_tdev); |
Der Rückgabewert ist von Null verschieden, wenn ein Medienwechsel gefunden wurde. Der Kernel ruft check_disk_change automatisch beim mount, aber nicht beim open auf.
Manche Programme greifen aber direkt auf die Daten zu, ohne das Gerät vorher einzuhängen. Dazu gehören beispielsweise fsck, mcopy und fdisk. Wenn der Treiber Statusinformationen über herausnehmbare Geräte im Speicher hält, sollte er check_disk_change aufrufen, wenn das Gerät zum erstenmal geöffnet wird. Die Kernel-Funktion ruft die normalen Treiber-Methoden (check_media_change und revalidate) auf, so daß in open selbst nichts Besonderes implementiert werden muß.
Hier die Implementation von open in sbull, die sich auch um Medienwechsel kümmert:
intsbull_open(structinode*inode,structfile*filp) { Sbull_Dev*dev;/*Geraeteinformation*/ intnum=MINOR(inode->i_rdev); if(num>=sbull_devs)return-ENODEV; dev=sbull_devices+num; spin_lock(&dev->lock); /*revalidierenbeimerstenopen,Fehler,wennkeineDatendasind*/ if(!dev->usage){ check_disk_change(inode->i_rdev); if(!dev->data) { spin_unlock(&dev->lock); return-ENOMEM; } } dev->usage++; spin_unlock(&dev->lock); MOD_INC_USE_COUNT; return0;/*Erfolg*/ } |
Im Treiber muß für Medienwechsel nichts weiter implementiert werden. Die Daten sind ohnehin beschädigt, wenn ein Medium gewechselt wird, während noch ein Prozeß das Gerät geöffnet hat. Ein Treiber kann solche Datenbeschädigung nur dann verhindern, wenn anhand des Verwendungszählers das Herausnehmen der Medien kontrolliert werden kann. In diesem Fall können open und close die Sperre entsprechend einschalten bzw. freigeben.