Heute möchte ich Ihnen etwas darĂŒber erzĂ€hlen, wie GEMDOS mit den Massenspeichern des ST (Diskette, Harddisk, RAM-Disk) umgeht, wenn es um das Lesen und Schreiben von Daten geht. DafĂŒr ist die unterste Ebene der GEMDOS-Dateiverwaltung zustĂ€ndig.
Als erstes wollen wir uns ein wenig um die Struktur der Speichermedien kĂŒmmern, auf denen sich bekanntlich neben den eigentlichen Dateien noch die Directories (Verzeichnisse) und die File Allocation Tables (FAT) befinden.
GEMDOS informiert sich ĂŒber die Aufteilung eines Mediums mit der BIOS-Funktion âGetbpbâ, die fĂŒr jedes Laufwerk einen Zeiger auf einen âBIOS Parameter Blockâ (BPB) liefert (Abb. 1). In Klammern sind die Werte fĂŒr Disketten im Standard-Format angegeben. Die âb_flagsâ werden bei Disketten vom BIOS fĂŒr interne Zwecke benutzt. GEMDOS interessiert sich nur fĂŒr âb_flags[0]â (s.u.).
typedef struct
{ int b_recsiz; /* Bytes pro Sektor (512) */
int b_clsiz; /* Sektoren pro Cluster' (2) */
int b_clsizb; /* Bytes pro Cluster (1024) */
int b_rdlen; /* Sektoren fUr Root Directory (7) */
int b_fsiz; /* Sektoren pro FAT (5) */
int b_fatrec; /* erste Sektornummer deiÂŁ zweiten FAT (6) */
int b_datrec; /* Sektornummer des ersten Daten-Clusters (18) */
int b_numcl; /* Anzahl Daten-Cluster auf Diskette (711) */
int b_flag[8]; /* Flags (Bit 0 von b_fXug[0]: FAT-Typ) (0) */
} BPB;
Abb. 1: Struktur des BIOS Parameter-Blocks (BPB)
Bei Disketten werden die Daten fĂŒr den BPB vom BIOS zwar aus dem Bootsektor gewonnen, doch GEMDOS interessiert sich ĂŒberhaupt nicht fĂŒr Bootsektoren, welche daher bei anderen Speichermedien auch ĂŒberhaupt nicht erforderlich sind.
Intern werden FAT und Directories ganz Ă€hnlich wie Dateien verwaltet, da so wesentliche Teile der Dateiverwaltung mitbenutzt werden können. Aus diesem Grund wird im folgenden meistens nur von âDateienâ gesprochen werden; gemeint sind aber auch FAT und Directories.
Sektoren und Cluster
Ein Speichermedium ist in Sektoren, die die kleinste Einheit bilden, auf die GEMDOS Zugriff hat, unterteilt. Wie die Sektoren physikalisch auf z.B. der Diskette verteilt sind, soll ganz das Problem des BIOS bzw. der Laufwerks-Treiber bleiben, daher werden sie mit einer âlogischen Sektornummerâ angesprochen.
FĂŒr das Lesen und Schreiben von Sektoren ist die BIOS-Funktion âRwabsâ zustĂ€ndig. Hier sind alle Sektoren einfach von Null bis zu einem Maximalwert durchnumeriert. Diese Art der Numerierung wird von mir als âBlOS-ZĂ€hlungâ bezeichnet. Daten-Sektoren werden zu sogenannten Clustern zusammengefaĂt. Ein Cluster besteht aus 1,2,4,.- (einer Potenz von 2) aufeinanderfolgenden Sektoren. FĂŒr jeden Cluster gibt es einen Eintrag in der FAT. Cluster werden von 2 an aufwĂ€rts durchnumeriert.
Viele Sektoren pro Cluster haben den Vorteil, daĂ die FAT wesentlich kleiner ist und daher schneller durchsucht werden kann. AuĂerdem arbeitet die unten erlĂ€uterte Pufferung von Sektoren bei wenigen FAT-Sektoren effektiver. Ein weiterer Vorteil ist die geringere âZersplitterungâ von Dateien, die sich darin Ă€uĂert, daĂ eine Datei aus kleinen, auf das Medium verstreuten âStĂŒckenâ, besteht.
Der Nachteil der Bildung von Clustern ist die gröĂere Verschwendung von Speicherplatz, da der fĂŒr eine Datei benötigte Platz immer auf ein Vielfaches der Cluster-GröĂe âaufgerundetâ werden muĂ.
Im BPB steht die Sektomummer des ersten Daten-Sektors (âb_datrecâ).
File Allocation Table (FAT)
Ăber die FAT werden die Daten-Cluster den Dateien zugeordnet. Jeder Eintrag eines Clusters gibt an, ob er unbenutzt oder defekt ist oder gibt die Nummer des nĂ€chsten zur Datei gehörenden Clusters bzw. das Dateiende an. Im Directory ist der erste Cluster einer Datei vermerkt.
Jeder Eintrag der FAT ist 12 oder 16 Bit lang, je nach Speichermedium. Das Format wird durch Bit 0 von 'b_flags[0]â des BPB bestimmt (gesetztes Bit fĂŒr 16-Bit-For-mat). Die genaue Struktur finden Sie in den âFloppy-Spielereienâ (ST 6/87, 1/88) ausfĂŒhrlich beschrieben (GruĂ zurĂŒck an Claus!).
Die ersten zwei EintrĂ€ge sind unbenutzt und werden von GEMDOS nicht beachtet (bei Standard-Disketten steht dort $F7FFFF). Dies ist wohl der Grund dafĂŒr, daĂ die Cluster-Numerierung bei 2 beginnt. Die LĂ€nge einer FAT steht im BPB unter âb_fsizâ.
GEMDOS verwaltet zwei identische FATs, wie schon aus der Struktur des BPB ersichtlich wird. Beim Schreiben eines FAT-Sektors wird er stets in beide FATs geschrieben, die somit jederzeit identisch sind. Gelesen wird jedoch immer aus der zweiten FAT. Die erste FAT ist also nur eine Sicherheitskopie, auf die bei einer beschĂ€digten zweiten FAT zurĂŒckgegriffen werden könnte. GEMDOS berĂŒcksichtigt dies allerdings nicht; man mĂŒĂte sich also selbst ein Programm schreiben, das eine defekte FAT durch die Kopie ersetzt. Es ist leider nicht möglich, auf eine FAT zu verzichten, sie mĂŒssen sogar unmittelbar hintereinander liegen. Im BPB ist explizit nur der Beginn der zweiten FAT vermerkt (âbjatrecâ). Die erste FAT muĂ direkt davor liegen, also bei 'b_fatrecâ minus âb_fsizâ.
Directories
Das Root Directory (Hauptverzeichnis) genieĂt eine Sonderstellung unter den Directories, da es eine feste GröĂe und einen festgelegten Platz auf dem Speichermedium hat.
Es beginnt nach der zweiten FAT. Seine LĂ€nge ist im BPB festgelegt (âb_rdlenâ). Aus der GröĂe eines Sektors (i.a. 512 Byte) und der eines Directory-Eintrags (32 Byte) ergibt sich die maximale Anzahl von EintrĂ€gen. Da alle Directory-Sektoren hintereinander liegen, braucht auf sie nicht mittels der FAT zugegriffen zu werden. Subdirectories werden dagegen wie normale Dateien behandelt. Sie haben einen Eintrag im Parent Directory, ihre Cluster liegen im Datenbereich der Diskette und werden daher auch mittels der FAT verwaltet. Ihre LĂ€nge wĂ€chst mit der Zahl der EintrĂ€ge und ist nur durch die KapazitĂ€t des Massenspeichers begrenzt. Ein Subdirectory wird jedoch nicht automatisch verkĂŒrzt, wenn Dateien gelöscht werden. Wenn von âDatenâ gesprochen wird, sind Subdirectories stets mit eingeschlossen; âDirectoryâ meint nur das Root Directory. Den genauen Aufbau eines Directories können Sie in den âFloppy-Spielereienâ (ST 8/87) nachlesen.
SektorzÀhlunq des GEMDOS
Um die Sache noch ein wenig zu komplizieren, zÀhlt GEMDOS die Sektoren intern nicht wie das BIOS, sondern hat seine eigene ZÀhlweise, in der die eben besprochene Strukturierung des Mediums zum Ausdruck kommt.
Daten-Sektoren haben in der GEMDOS-ZĂ€hlung positive Sektornummem. Die Nummer des ersten Sektors des ersten Clusters (also von Cluster 2) ist zwei mal die Anzahl der Sektoren pro Cluster.
Die Sektoren des Root Directories und der FAT haben bei der GEMDOS-ZĂ€hlung negative Nummern. Sie werden nach einem komplizierten Verfahren aus den Daten des BPB bestimmt, worauf wir erst nĂ€chsten Monat zurĂŒckkommen werden. Hier seien als Beispiel nur die Werte fĂŒr Disketten im Standard-Format angegeben (Abb. 2). Wenn Sie es nicht abwarten wollen, können Sie ja versuchen, das âSystemâ zu erraten (viel SpaĂ!).
Abb. 2: Aufteilung einer Standard-Diskette
Bei der ZĂ€hlung der Datensektoren sind sich BIOS und GEMDOS allerdings nicht ganz einig. Der âb_numcT-Wert des BPB gibt die Gesamtzahl der vorhandenen Daten-Cluster an. GEMDOS ist der Meinung, dies sei die Nummer des letzten Daten-Clusters in seiner ZĂ€hlweise. Da GEMDOS die Cluster von 2 an zĂ€hlt, rechnet es mit zwei Clustern weniger, als eigentlich da sind. Dies ist durchweg bei allen internen GEMDOS-Routinen der Fall, so daĂ die letzten zwei Cluster eines Mediums von GEMDOS ungenutzt bleiben. Bei Disketten werden somit vier Sektoren (= 2 kB) Speicherplatz verschenkt.
typedef BCB
{ BCB *b_link; /* Zeiger auf nÀchsten BCB dieser Liste */
int b_bufdrv; /* Laufwerksnummer-, -1 fĂŒr' ungĂŒltigen BCB */
int b_buftyp; /* FAT (0), DIR (1), DATA (2) */
int b_bufrec; /* Sektor-Nummer in GEMDOS-ZĂ€hlung */
int b_dirty; /* ungleich Null: Pufferinhalt geÀndert */
DMD *b_dmd; /* Zeiger auf DMD von b_bufdrv */
char *b_bufr; /* Zeiger auf eigentlichen Sektor-Puffer */
} BCB;
Abb. 3: Struktur des Buffer-Control-Blocks (BCB)
Das Konzept der Sektor-Pufferung
Damit Sie auch wissen, warum ich Ihnen dies alles so genau erklÀrt habe, kommen wir nun zur Anwendung dieser Grundlagen, indem wir uns ansehen, wie GEMDOS nun eigentlich seine Zugriffe auf Massenspeicher abwickelt.
GEMDOS hat drei allgemeine Routinen zum Lesen bzw. Schreiben von Sektoren auf bzw. von Massenspeichern. Sie sind auch fĂŒr die Ăbersetzung der GEMDOS-ZĂ€hlung in die BlOS-ZĂ€hlung (mit Hilfe des DMD) zustĂ€ndig. Die ĂŒbergeordneten Dateifunktionen kennen also nur GEMDOS-Sektornummern.
Es mĂŒssen einzelne Sektoren ĂŒbertragen werden können, von denen nur einige Zeichen benötigt bzw. geĂ€ndert werden sollen (vor allem bei FAT und Directories). Um zu verhindern, daĂ sie bei jedem Zugriff erneut geladen werden mĂŒssen, was eine ziemliche Zeitverschwendung wĂ€re, ist es sinnvoll, solche Sektoren zwischenzuspeichern.
GEMDOS verwaltet hierfĂŒr zwei Pufferlisten, eine fĂŒr FAT-Sektoren, die andere fĂŒr Directory- und Daten-Sektoren. Mit Directory-Sektoren sind hier wieder nur die Sektoren des Root Directory gemeint. Zu jedem Puffer existiert ein sogenannter Buffer Control Blockâ (BCB). Jeder BCB enthĂ€lt Angaben ĂŒber den zugehörigen Sektor (Abb. 3), damit GEMDOS den Ăberblick behĂ€lt (genauer gesagt: es versucht). Dazu gehören die logische Sektornummer in GEMDOS-ZĂ€hlung, die Laufwerkskennung (0...15), der Puffer-Typ (0,1.2 fĂŒr FAT-, Directory- bzw. Daten-Sektoren) und die Adresse des eigentlichen Puffers, wo der Inhalt des Sektors zu finden ist.
Eine Laufwerkskennung von -1 gibt an, daĂ der Puffer zur Zeit unbenutzt ist. Damit sind die anderen Daten des BCB bis auf âb_bufrâ ungĂŒltig.
Des weiteren gibt es ein âDirty-Flagâ. Wenn es ungleich Null ist, wurde der Sektor geĂ€ndert und ist noch nicht auf das Laufwerk zurĂŒckgeschrieben worden. ZusĂ€tzlich zu seiner Kennung wird das Laufwerk noch durch den âDrive Media Descriptorâ (DMD) identifiziert, bei dem ich Sie erneut auf die nĂ€chste Folge vertrösten muĂ.
Alle BCBs einer Pufferliste sind miteinander verkettet, d.h. âb_linkâ zeigt auf den nĂ€chsten BCB, beim letzten BCB einer Liste ist âb_linkâ gleich 0L (Abb. 4).
Die AnfĂ€nge der beiden Listen ist in der globalen Systemvariablen âbuff ($4B2) vermerkt: âbufl[0]\ also $4B2, enthĂ€lt den Anfang der FAT-Liste, âbufl[l']â, also $4B6, zeigt auf den ersten BCB der DIR/ DATA-Liste.
Die Sektoren sind in der Reihenfolge des letzten Zugriffs in der Liste sortiert. Der erste Sektor ist der zuletzt angesprochene, usw.
Abb. 4: Struktur der GEMDOS-Pufferlisten (Beispiel)
Sektor ĂŒber Pufferliste ĂŒbertragen
Zum Lesen eines einzelnen Sektors ĂŒber die Pufferliste dient die im folgenden âf_sreadâ genannte interne Routine. Sie findet auch bei Schreibzugriffen Verwendung, da auch dort der Sektor vor seiner Ănderung erst einmal geladen werden muĂ.
Falls der zu lesende Sektor schon in der Pufferliste vorhanden ist, wird mittels der BIOS-Funktion 'Mediachâ geprĂŒft, ob das Speichermedium (i.a. die Diskette) gewechselt wurde. Bei einem âsicheren Mediumwechsel" wird die ganze GEMDOS-Funktion sofort mit der BIOS(! (-Fehlermeldung E_CHNG (-14) abgebrochen, wie in der Januar-Ausgabe beschrieben.
Tritt ânurâ ein âmöglicher Mediumwechselâ auf, so wird der Sektor einfach nochmal geladen, allerdings ohne RĂŒcksicht darauf, ob er schon geĂ€ndert wurde (âDirtyâ-Flag)l Allerdings wird der âmögliche Mediumwechselâ vom BIOS nur (?) bei Disketten mit Schreibschutz gemeldet (dort allerdings fast immer!), so daĂ dieser Fall nicht eintreten sollte.
Wenn der Sektor noch nicht gepuffert ist, wird er in einen freien Puffer der jeweiligen Liste geladen. Ist keiner mehr unbenutzt, so wird der âĂ€ltesteâ Puffer (also der letzte) aus der Liste entfernt. Dadurch wird erreicht, daĂ die am hĂ€ufigsten benötigten Sektoren am lĂ€ngsten gepuffert bleiben. Dabei wird der Sektor natĂŒrlich zurĂŒckgeschrieben, falls er geĂ€ndert wurde. Er wird mit "BIOS-Rwabsâ gelesen. Tritt hierbei ein Fehler auf. wird die GEMDOS-Funktion wie ĂŒblich abgebrochen.
Der zu lesende Sektor wird konsequenterweise in jedem Fall an die erste Stelle der Liste gehĂ€ngt, egal, ob er tatsĂ€chlich geladen wurde oder schon vorhanden war. Am Ende von âf_sreadâ wird das "Dirty-Flagâ gesetzt, wenn ein Schreibzugriff geplant ist. Die ĂŒbergeordneten Funktionen zum Schreiben rufen âf_sreadâ kurz vorher auf, so daĂ diese Methode gerechtfertigt ist. Dieses Verfahren hat den Vorteil, daĂ nur die auf der untersten Ebene angesiedelten Funktionen fĂŒr die Pufferliste sich mit den Details der BCBs herumschlagen mĂŒssen.
Nun sind auch noch ein paar Worte zur oben schon erwĂ€hnten Routine zum Schreiben einzelner Sektoren (âf_swriteâ) angebracht. Sie wird auĂer bei âf_sreadâ immer dann gebraucht, wenn explizit bestimmte Sektoren zurĂŒckgeschrieben werden sollen (z.B. beim SchlieĂen einer Datei). Bei FAT-Sektoren finden die Schreibzugriffe fĂŒr die beiden identischen FATs unmittelbar nacheinander statt. Bemerkenswert ist, daĂ vor dem eigentlichen Schreibvorgang (mit Rwabs) der Puffer ungĂŒltig gemacht (b_btifdrv = -1). und erst danach wieder fĂŒr gĂŒltig erklĂ€rt wird (auĂerdem wird âb_dirtyâ natĂŒrlich gelöscht). Dies hat zur Folge, daĂ nach einem Abbruch der GEMDOS-Funktion bei einem Schreibfehler der Puffer ungĂŒltig ist, d.h. daĂ sein Inhalt als verloren angesehen wird.
In einigen FĂ€llen wird âf_swriteâ auch mit einem ungĂŒltigen oder nicht verĂ€nderten Puffer aufgerufen. Diese werden zwar nicht geschrieben, wie es auch selbstverstĂ€ndlich sein sollte, aber verĂ€nderte werden ungĂŒltig gemacht. Dies hat weitreichende Folgen, wie Ihnen bald klar werden wird.
Direkter Sektor-Zugriff
Wenn alle Zugriffe nach dem oben beschriebenen Verfahren ablaufen wĂŒrden, wĂ€re das Laden von Programmen vermutlich genauso langsam wie das Lesen von Texten mit 1st Word+.
Daher gibt es eine weitere elementare Routine fĂŒr den Massenspeicher-Zugriff, die fĂŒr das Ăbertragen einer zusammenhĂ€ngenden Folge von Sektoren zustĂ€ndig ist (âf_mrwâ). Sie arbeitet also wie âRwabsâ auf BIOS-Ebene, hat Ă€hnliche Parameter und ruft im Prinzip diese Funktion direkt auf.
Hier mĂŒssen allerdings Kollisionen mit der Pufferliste berĂŒcksichtigt werden. Aus diesem Grund werden alle Sektoren der Pufferlisten, die nun durch âf_mrwâ ĂŒbertragen werden sollen, zuerst mit âf_swriteâ zurĂŒckgeschrieben. Dann erst erfolgt die Ăbertragung mit âRwabsâ.
Beim Lesen wird dadurch sichergestellt, daĂ Ănderungen in der Pufferliste nicht unter den Tisch fallen. Der Schreibzugriff könnte zwar vermieden werden, indem âf_mrwâ sich die geĂ€nderten Sektoren aus der Pufferliste holt, doch soviel MĂŒhe wollten sich die GEMDOS-Programmierer mit âf_mrwâ offensichtlich nicht machen. Beim Schreiben ist dies sogar ganz ĂŒberflĂŒssig, da der Sektor ja sowieso gleich ganz neu geschrieben wird. Hier wĂ€re es allerdings wichtig, den entsprechenden Puffer ungĂŒltig zu machen (wenn er schon nicht auf den neuesten Stand gebracht wird), da nachfolgende Schreibzugriffe ĂŒber die Pufferliste sonst auf den alten Sektorinhalt gehen. Wie wir gerade gesehen haben, wird dies von âf_swriteâ aber nur gemacht, wenn der Sektor nicht geĂ€ndert wurde.
Und GEMDOS ist doch nicht fehlerfrei
Welche fatalen Folgen dies haben kann, sehen Sie an Listing 1. Ihre Kenntnisse in C oder einer Ă€hnlichen Sprache sollten Ihnen sagen, daĂ nach Ablauf dieses Programms in der Datei âTestâ ein âcâ und 511 âbâs stehen. Ein kurzer Blick in âTestâ nachdem Programmlauf wird Ihnen allerdings ein âcâ und 511 âaâs bescheren! Die ErklĂ€rung dĂŒrfte nach den vorangegangenen ErlĂ€uterungen und den Kommentaren im Listing nicht schwerfallen.
Moral von der Geschichte: Benutzen Sie âFwriteâ immer nur fĂŒr Datenmengen gröĂer oder kleiner als 512 Byte. Eine âMischungâ ist allerdings erlaubt, wenn Sie rein sequentiell arbeiten, also kein âFseekâ verwenden.
Hiermit dĂŒrfte klar geworden sein, daĂ die âZusammenarbeitâ zwischen den beiden Arten des internen Datenzugriffs nicht gerade besonders gut ist. GlĂŒcklicherweise treten diese Fehler in der Praxis oft nicht auf. da die Pufferliste hauptsĂ€chlich bei FAT und Directories in Erscheinung tritt, der Mehr-Sektor-Zugriff vornehmlich bei gröĂeren Dateien.
Es gibt noch einen weiteren Fehler im Zusammenhang mit der Pufferliste. Unter bestimmten, nicht geklĂ€rten UmstĂ€nden hat plötzlich ein Daten-Sektor der Pufferliste die GEMDOS-Sektornummer 0. Diese Sektornummer kommt ja normalerweise gar nicht vor, was beim ZurĂŒckschreiben aber nicht bemerkt wird. Bei 2 Sektoren pro Cluster hat der erste Daten-Sektor die GEMDOS-Nummer 4, also wird dieser âSektor 0" auf den vierten Sektor vor den ersten Daten-Sektor geschrieben. Beim Standard-Diskettenformat ist dies der viertletzte von sieben Sektoren des Root Directorys (RD). Da das RD selten mehr als 48 EintrĂ€ge (das sind drei Sektoren) hat, merkt man von diesem Fehler normalerweise nichts.
Bei der Programmierung eines RAM-Disk-Treibers war ich nun zufĂ€llig der Meinung, vier RD-Sektoren wĂŒrden â s auch tun. Daraufhin ĂŒberschrieb mir GEMDOS regelmĂ€Ăig meinen ersten RD-Sektor... und ich brauchte zwei Tage, um den Fehler GEMDOS und nicht dem RAM-Disk-Treiber zuzuschreiben. Auch auf einigen Disketten konnte ich mittels eines Diskettenmonitors einen vermurksten vierten RD-Sektor finden.
Bei mir trat dieser Fehler immer nur auf, wenn ein Programm Schreibzugriffe in âkleinen Einheitenâ (also ĂŒber die Pufferliste) machte und dabei das Speichermedium voll wurde. Aber vielleicht weiĂ jemand von Ihnen ja mehr darĂŒber?
Datei-Zugriff
Nachdem wir nun die elementaren Zugriffsroutinen besprochen haben, und Sie hoffentlich noch interessiert dabei sind, geht es nun um den Zugriff auf Dateiebene. Dazu gibt es eine umfangreiche Routine(vonmir âf_frwâ genannt), ĂŒber die alle Dateizugriffe laufen. In der Programmhierarchie direkt darĂŒber âsitzenâ die GEMDOS-Funktionen âFreadâ und âFwriteâ, so daĂ Sie eine Vorstellung davon haben, was von âf_frwâ geleistet werden muĂ - nĂ€mlich die Umsetzung der relativen Datei-Positionen in die logischen Sektornummem (GEMDOS-ZĂ€hlung), die von âf_sreadâ & Co. verstanden werden. Es muĂ ferner möglich sein, von einer beliebigen Position innerhalb einer Datei eine beliebige Anzahl von Zeichen zu ĂŒbertragen.
Die Parameter sind Ă€hnlich âFreadâ/ âFwriteâ, statt des Datei-Handles wird ein interner âFile Descriptorâ, der auch fĂŒr Directories usw. existiert, ĂŒbergeben (dazu mehr in einer spĂ€teren Folge).
Aus der aktuellen Dateiposition und den Laufwerks-spezifischen Daten wie Cluster-GröĂe usw., die im DMD stehen, wird die logische Sektornummer und die Position des ersten Zeichens, auf das zugegriffen werden soll, errechnet.
Im allgemeinen wird der Zugriff mitten in einem Sektor beginnen. Falls dies der Fall ist, wird er mit âf_sreadâ ĂŒber die Pufferliste geladen. Der tatsĂ€chlich interessierende Teil der Daten wird dann vom bzw. zum vom Aufrufer bereitgestellten Speicherbereich kopiert. Falls alle zu ĂŒbertragenden Bytes in diesem Sektor liegen, ist alles erledigt.
Ansonsten werden alle nun folgenden Sektoren, die komplett ĂŒbertragen werden mĂŒssen (das sind also alle restlichen, eventuell bis auf den letzten), gelesen bzw. geschrieben. Hierbei entfĂ€llt der Umweg ĂŒber die Pufferliste.
Zuerst werden die restlichen Sektoren bis zum Ende des aktuellen Clusters auf einen Schlag mit âf_mrwâ ĂŒbertragen. Dies ist möglich, da alle Sektoren eines Clusters in der logischen Sektornumerierung aufeinander folgen.
Nun kommen die komplett zu lesenden Cluster dran. Dabei kann es natĂŒrlich Vorkommen, daĂ die Cluster ĂŒber das Medium verstreut sind. GEMDOS ist nun so schlau, die Cluster in möglichst groĂen Gruppen zu ĂŒbertragen, was die Geschwindigkeit erhöht. Es geht die Cluster-Nummern, die es aus der FAT holt, durch, bis es eine âLĂŒckeâ entdeckt. Dann wird die auf diese Weise ermittelte zusammenhĂ€ngende Cluster-Gruppe in einem Rutsch ĂŒbertragen.
Beim letzten Cluster wird es im allgemeinen so sein, daĂ nicht alle Sektoren gebraucht werden. Diese werden separat behandelt, genauso wie die letzten Sektoren des ersten âangebrochenenâ Clusters. Auch hierbei wird natĂŒrlich direkt ĂŒber âf_mrwâ gearbeitet.
Zu guter Letzt bleibt unter UmstĂ€nden ein Sektor ĂŒbrig, bei dem nur auf den ersten Teil zugegriffen werden soll. Dies geschieht wie beim ersten Sektor ĂŒber die Pufferliste und anschlieĂendes Kopieren. ZurĂŒckgegeben wird die Anzahl der tatsĂ€chlich ĂŒbertragenen Bytes. Dies wird von 'Fread' und âFwriteâ direkt an den Aufrufer zurĂŒckgegeben. Bei voller Diskette oder Dateiende wird einfach abgebrochen, so daĂ dies beim Vergleich des RĂŒckgabewertes mit der gewĂŒnschten Zahl der zu schreibenden Zeichen bemerkt werden kann.
Sie sehen also, daĂ GEMDOS hier recht wirkungsvoll arbeitet, insbesondere, wenn groĂe Datenmengen auf einmal ĂŒbertragen werden.
Bei der Ăbertragung ganzer Cluster wird die FAT gelesen, um aus der Dateiposition die Clusternummer zu bestimmen. Da ja auch die FAT intern wie eine Datei verwaltet wird, geschieht dies mit einer Routine, die letztendlich wiederum âf_frw" aufruft. Da bei FAT-Zugriffen aber immer nur einzelne Bytes gelesen werden, bricht diese Rekursion immer hier schon ab.
Das Lesen von ganzen Dateien mit nur einem âFreadâ ist daher sehr schnell; eine Verzögerung tritt dann auf, wenn ein neuer FAT-Sektor, der nicht in der Pufferliste vorhanden ist, geladen werden muĂ. Programme, die Dateien in sehr kleinen Portionen, im Extremfall byte-weise, lesen, sind nicht langsam, weil die Sektoren zu oft geladen werden (dies wird durch die Pufferliste ja verhindert), sondern weil das ganze Drumherum einfach zu lange dauert. Bis das Programm das letzte Byte verarbeitet, den nĂ€chsten Aufruf von âFreadâ gemacht und GEMDOS sich bis zu der Stelle vorgekĂ€mpft hat, wo der Sektor wirklich geladen wird, ist so viel Zeit vergangen, daĂ das BIOS den nĂ€chsten Sektor gerade verpaĂt hat (die Diskette dreht sich bekanntlich immer weiter). Und bis der Sektor sich mal wieder unter dem Schreib-Lese-Kopf vorbeibewegt, dauert es schon eine Weile.
Anwenderprogramme haben normalerweise die Möglichkeit, die Dateizugriffe in groĂen Einheiten durchzufĂŒhren, GEMDOS selbst muĂ jedoch bei FAT- und Directory-Operationen immer auf einzelne FAT- bzw. Directory-EintrĂ€ge (2 bzw. 32 Bytes) zugreifen.
Im Falle des Directories wird hier noch ein wenig zeitsparender verfahren. Dazu wird âf_frwâ eine Null als Adresse des fĂŒr die Ăbertragung benötigten Speicherbereichs ĂŒbergeben. Daraufhin wird die Anfangsadresse der zu ĂŒbertragenden Daten im Sektorpuffer zurĂŒckgeliefert (an Stelle der Zahl der ĂŒbertragenen Zeichen), nachdem der Sektor ĂŒber âf_sreadâ geladen wurde. Das Kopieren der Daten aus dem Puffer heraus entfĂ€llt also.
Dies ist nur möglich, wenn die Daten garantiert alle innerhalb des gleichen Sektors liegen, da nach dem Laden des ersten Sektors auf jeden Fall abgebrochen wird. Bei Directory-Sektoren ist dies der Fall, da in jeden Sektor genau 16 EintrĂ€ge (32*16=512 Byte) passen. AuĂerdem muĂ gewĂ€hrleistet sein, daĂ die Daten auch möglichst bald verarbeitet werden, da unter UmstĂ€nden schon beim nĂ€chsten Dateizugriff der Sektor aus der Pufferliste entfernt wird.
Bei der FAT ist dies nicht möglich, da durch das komplizierte Format ein Eintrag bei einer 12-Bit-FAT auch auf zwei Sektoren verteilt sein kann. Dies ist wohl ein Grund dafĂŒr, warum gerade FAT-Zugriffe bei GEMDOS sehr langsam sind. Ăbrigens lĂ€Ăt sich dieser Spezial-Modus auch von eigenen Programmen aus verwenden, da man ĂŒber âFreadâ direkten Zugang zu âf_frwâ hat. Da dies aber nicht dokumentiert ist und bei zukĂŒnftigen TOS-Versionen nicht mehr zu funktionieren braucht, sollt man davon absehen; auĂerdem gibt es wohl auch nur wenige Anwendungen, die davon Gebrauch machen könnten.
GröĂe von Sektoren und Clustern
Beim ST haben Sektoren immer eine GröĂe von 512 Byte, und Cluster bestehen immer aus zwei oder einem Sektor. GEMDOS erlaubt theoretisch auch andere GröĂen (jeweils Potenzen von 2), entsprechende Versuche fĂŒhren allerdings nur zu MiĂerfolgen.
Die entsprechenden Daten des BPB werden zwar korrekt interpretiert und intern wird mit ihnen auch richtig gerechnet, um Datei-Positionen usw. zu bestimmen.
Die Begrenzung der SektorgröĂe liegt in der einheitlichen GröĂe der Sektorpuffer. GEMDOS kennt deren GröĂe nĂ€mlich nicht. Da jeder Puffer fĂŒr Sektoren eines jeden Laufwerk in Frage kommt, mĂŒssen alle Puffer die im System maximal vorkommende SektorgröĂe haben. Die vom BIOS installierten Puffer sind - wie nicht anders zu erwarten war - nur 512 Byte groĂ.
GEMDOS hat ĂŒbrigens keine absoluten Zeiger auf BCBs oder Sektorpuffer. Daher ist eine Umgestaltung der Pufferlisten jederzeit möglich (natĂŒrlich nicht wĂ€hrend der Abarbeitung einer GEMDOS-Funktion). Die Verwendung eines Massenspeichers, der mit 1024-Byte-Sektoren arbeitet, ist also durchaus möglich; das Treiberprogramm muĂ bei seiner Installation nur die Standard-Sektorpuffer gegen eigene der richtigen GröĂe austauschen. Man kann nur hoffen, daĂ es keine weiteren Schwierigkeiten gibt.
Cluster mit mehr als zwei Sektoren scheitern an einem Fehler in âf_frwâ. Ein Patch zur Behebung dieses Fehlers brachte allerdings nicht den erhofften Erfolg, so daĂ hierzu noch nicht das letzte Wort gesprochen ist.
Verwaltung der FAT
Zu den Routinen zur FAT-Verwaltung gibt es nicht allzuviel anzumerken. Sie sind in der Lage, Folge-Cluster, freie Cluster usw. zu ermitteln. Zum eigentlichen Zugriff auf die FAT wird âf_frwâ aufgerufen. Einzig und al lein die Fehler machen wieder einmal zu schaffen. Der erste Fehler betrifft das Indizieren eines Eintrages in der FAT an Hand seiner Cluster-Nummer. Bei Cluster-Nummern gröĂer als $3FFF (also nur bei 16-Bit-FATs) geht das schief. Bei zwei Sektoren pro Cluster ergibt sich somit die maximal erlaubte GröĂe eines Mediums zu 16 MB. Dieser Fehler wurde im Blitter-TOS sogar korrigiert (Cluster-Nummern bis $7FFF möglich)!! Allerdings möchte ich nicht dafĂŒr garantieren, daĂ Harddisk-Partitions bis 32 MB jetzt möglich sind, da es vielleicht weitere âHindernisseâ gibt. Der zweite Fehler tritt nur bei 12-Bit-FATs auf. Er fĂŒhrt dazu, daĂ FAT-EintrĂ€ge gröĂer als $7FF falsch ausgewertet werden (GEMDOS arbeitet mit $Fxxx statt $0xxx weiter). Dies passiert aber nur bei ungeraden Clustem(!). Auch die Datei-Ende-Kennung $FFF wird nicht erkannt, aber sie wird zufĂ€llig(l) richtig weiterverarbeitet. Bei zwei Sektoren pro Cluster wird somit ânurâ die KapazitĂ€t auf 2 MB statt möglicher 4 MB beschrĂ€nkt.
Beide Fehler resultieren ĂŒbrigens aus fĂŒr C typischen Vorzeichen-Fehlern.
Es sind nur $FFF bzw. $FFFF als Dateiende-Marke gedacht. Es gibt keine weiteren besonderen FAT-EintrÀge (wie $FF0-$FFE bei PC-DOS).
Nutzbarkeit der Sektor-Pufferung
Nach diesen AusfĂŒhrungen ĂŒber GEMDOS fragen Sie sich vielleicht schon verzweifelt, wamm Sie (fast) noch nie etwas von der Pufferung bemerkt haben, warum z.B. beim Ăffnen oder SchlieĂen eines Ordners das Directory jedesmal neu von Diskette gelesen wird.
Die Ursachen hierfĂŒr sind ĂŒber die verschiedensten Teile des Betriebssystems verstreut, fangen wir also beim BIOS an. Beim Systemstart ist das BIOS fĂŒr das Anlegen der (leeren) Pufferlisten verantwortlich. GEMDOS verwaltet diese nur, Ă€ndert aber nie die GröĂe der Listen. Da bei anderen Gelegenheiten krĂ€ftig mit dem Speicherplatz geaast wurde, sollte wohl hier ein wenig gespart werden. Beide Listen bestehen nĂ€mlich nur aus jeweils zwei Sektoren, wodurch der Nutzeffekt der Sektor-Pufferung fast verschwindet. Dieser Umstand lĂ€Ăt sich relativ leicht beheben, da die Systemvariable âbuflâ legal zugĂ€nglich ist. Das Programm 'EXTBUFLâ (Listing 2) erlaubt es, beide Listen beliebig zu erweitern. Es kann sogar mehrmals hintereinander gestartet werden, da es nur den fĂŒr die Erweiterung benötigten Speicherplatz verbraucht und sich selbst vollstĂ€ndig freigibt.
Doch auch nach Einrichtung geradezu riesiger Pufferlisten wird Ihre Begeisterung sich in Grenzen halten.
~~~~~~~~~~~~~~~~~~~~~
Adresse
RAM-TOS ROM-TOS ROM-TOS Bytes (in Hex)
6.2.86 6.2.86 22.4.87
Fehler in 'f_mrw'
00b62a fc579c fc5a54 2a 78 04 b6 60 30 20 6e 00 12 30 28 00
06 bO 6d 00 04 66 20 30 2d 00 08 32 2e
00 Oc bO 41 6d 14 d2 6e 00 0a b2 40 6f
0c 2e 8d 61 00 fe e4 3b 7c ff ff 00 04
00b663 fc57d5 fc5a8d cc
Fehler bei 12-Bit-FAT
00bd03 fc5e75 fc612b 4f
Fehler bei 16-Bit-FAT
00bb98 fc5dOa â----- 7c 00 3c 07 e3 86 4e 71
**Abb. 5: Patch fĂŒr TOS-Fehler**
</div>
Bei Disketten mit Schreibschutz Ă€ndert 'ich ĂŒberhaupt nichts, da BIOS hier immer (sobald eine bestimmte Zeitspanne nach dem letzten Diskettenzugriff vergangen :-t) einen âunsicheren Diskettenwechselâ meldet, so daĂ die Puffer wie bei âf_sreadâ beschrieben immer neu geladen werden. Bei Disketten ohne Schreibschutz werden Sie eine Verbesserung bemerken, wenn Sie >;ch z.B. in einer Fileselector-Box befinden. Hier können Sie nun fleiĂig Ordner auf- und zumachen, ohne sich ĂŒber ein anlaufendes Floppylaufwerk zu Ă€rgern.
Auf dem Desktop hat sich wiederum nichts geĂ€ndert. Vor den meisten (vielleicht sogar allen) Diskettenoperationen âsuggeriertâ das Desktop dem BIOS nĂ€mlich einen âsicheren Diskettenwechselâ! Daraufhin gibt GEMDOS die gesamte Pufferliste frei, also Pufferung ade! Mit dieser zweifelhaften Methode wird ĂŒber die UnzulĂ€nglichkeiten der BlOS-Diskettenwechsel-Erkennung hinweggetĂ€uscht, wobei die Probleme jetzt auf die Anwenderprogramme verlagert werden, die nicht auf diese Scheinlösung zurĂŒckgreifen (wollen oder können). Dies erklĂ€rt, warum das Desktop so "hervorragendâ (verglichen mit anderen Programmen) mit Diskettenwechseln zurechtkommt.
Nun könnten die Harddisk-Benutzer frohlocken, da es bei ihnen keine Medienwechsel gibt. Und siehe da, das Desktop macht keine Schwierigkeiten mehr.
Doch halt! Wer sich noch an die Beschreibung von âf_swrite' erinnert, wird wissen, daĂ nicht verĂ€nderte Sektoren (was wohl der Normalfall sein dĂŒrfte) fĂŒr ungĂŒltig erklĂ€rt werden. Nach jedem âFcloseâ, welches alle Sektoren mit âfswriteâ zurĂŒckschreibt, ist die Pufferliste somit leer. Das passiert unter anderem beim Kopieren und Laden von Programmen, so daĂ die Sektor-Pufferung auch bei Harddisk nur begrenzte Erfolge bringt. Trotzdem kann es unter UmstĂ€nden sinnvoll sein, die Pufferliste fĂŒr die Benutzung bestimmter Programme zu vergröĂern. Dabei sollten Sie vor allem die damit zusammenhĂ€ngenden GEMDOS-Fehler beachten. Einige Programme mit raffinierten Dateizugriffen sorgen da womöglich fĂŒr einige Ăberraschungen.
Falls Sie selbst ein wenig mit diesen Problemen herumspielen wollen, können Sie dies mit âSHOWBUFLâ (Listing 3) tun. Dieses Accessory zeigt den aktuellen Zustand der Pufferlisten an. Die Ausgaben stammen direkt aus den BCBs, bis auf â sec â. Hierbei handelt es sich um die mittels des DMD aus der GEMDOS-Sektornummer errechnete BlOS-Sektornummer. Nicht-Harddisk-Besitzer können auch mit einer RAM-Disk vorlieb nehmen, da fĂŒr diese das Gleiche wie fĂŒr Harddisk gilt, nur daĂ man von der Pufferung auf Grund ihrer Schnelligkeit nichts merkt.
# Ănderungen des TOS
In Abb. 5 sind Patches fĂŒr TOS-Fehler angegeben. Dabei handelt es sich um die oben beschriebenen Fehler in der internen Funktion âf_mrwâ und der FAT-Verwaltung.
âf_mrwâ macht nun alle mit âf_swriteâ geschriebenen Puffer ungĂŒltig, egal ob sie geĂ€ndert waren oder nicht.
# SchluĂbemerkung
So, damit wĂ€ren wir fĂŒr heute mal wieder am Ende angekommen. NĂ€chsten Monat beschĂ€ftigen wir uns u.a. mit der Verwaltung der Laufwerke, wozu auch die versprochene ErklĂ€rung des DMD gehört.
<div class="textkasten" markdown=1>
/* Demonstrationsprogramm fĂŒr Fehler in der Verwaltung der GEMDOS-Pufferliste */
#include <osbind.h>
#define NAME "test"
main()
{ int fh; /* Datei-Handle */
int i; char c;
char buf[512];
if ((fh = Fcreate(NAME,0)) < 0) return;
/* Datei lĂ€Ăt sich nicht öffnen: Abbruch */
c = ' a' ;
for(i=0; i<512; i++)
/* GEMDOS schreibt dies ĂŒber Pufferliste */
Fwrite(fh,1L, &c);
/* (ein Zeichen wĂŒrde auch reichen) */
Fseek(0L,fh,0);
/* zurĂŒck zum Dateianfang */
for(i=0; i<512; i++)
/* 'buf' voller 'b's schreiben */
buf[i] = 'b';
Fwrite(fh,512L,buf);
/* GEMDOS schreibt dies direkt
...und lÀ_t Puffer so wie er war! */
Fseek(0L,fh,0); /* zurĂŒck zum Dateianfang */
c = 'c';
/* ein 'c' schreiben (ĂŒber Pufferliste) */
Fwrite(fh,1L,&c);
/* Datei sollte jetzt 1 'cr und 255
'b's enthalten, aber... */
Fclose(fh);
/*GEMDOS schreibt jetzt Puffer mit 'b's zurĂŒck*/
}
</div>
<div class="textkasten" markdown=1>
/* Erweiterung der GEMDOS-Pufferlisten 24.1.1988 by A. Esser
entwickelt mit MEGAMAX C */
#include <osbind.h>
#define bufl (BCB *)0x4b2L
/ Systemvariable 'bufl' */
/* Definition BCB */
typedef struct bctrl
{ struct bctrl b_link;
int b_bufdrv;
int b_buftyp;
int b_bufrec;
int b_dirty;
long b_dmd;
/ korrekt: 'DMD *b_dmd', aber DMD hier nicht def. */
char *b_bufr;
} BCB;
int install(buflp,nsec)
BCB **buflp;
int nsec;
{ long mp;
int i;
BCB *bcb;
/* Speicherplatz fĂŒr 'nsec' BCBs und Puffer reservieren */
if ( (mp = Malloc((long)nsec* (512+sizeof(BCB)))) == 0L)
{ puts("Nicht genug Speicher.");
return -1; -
}
for (i=0; icnsec; i++)
{ bcb = (BCB *)mp;
/* BCB initialisieren */
bcb->b_bufdrv = -1;
/* Puffer ungĂŒltig */
bcb->b_buftyp = -1;
/* kein gĂŒltiger Puffer-Typ */
bcb->b_bufrec =0;
/* keine gĂŒltige Sektor-Nummer */
bcb->b_dirty = 0;
/* nichts geÀndert */
bcb->b_dmd = 0L;
/* BCB keinem Laufwerk zugeordnet */
/* Puffer direkt hinter BCB legen */
bcb->b_bufr = (char *)(mp+sizeof(BCB));
/* BCB vorne in gewĂŒnschte Puffer-Liste einhĂ€ngen */
bcb->b_link = *buflp;
*buflp = beb;
mp += 512+sizeof(BCB);
/* weiter zum nÀchsten */
}
return 0;
}
main()
{ int nsec;
long stack;
/* gemerkter Supervisor-Stackpointer /
stack = Super(0L);
/ Supervisor-Mode notwendig /
puts("\rWieviele zusÀtzliche FAT-Puffer?");
scanf("%d",&nsec);
install(bufl,nsec);
/ Puffer fĂŒr FAT nach Liste 'bufl[0]' /
puts("\rWieviele zusÀtzliche DIR/DATA-Puffer?");
scanf("%d",&nsec);
install (buf1+1,nsec);
/ Puffer fĂŒr DATA/DIR nach Liste'*â'buf 1 [1] ' /
Super(stack);
/ zurĂŒck nach User-Mode /
/ Programm komplett freigeben, aber reservierten Speicher behalten */
Ptermres(0L,0);
}
</div>
<div class="textkasten" markdown=1>
/* Accessory zur Anzeige der GEMDOS-Pufferlisten 24.1.1988 by A. Esser
entwickelt mit MEGAMAX C */
#include <osbind.h>
#include <gemdefs.h>
#include <obdefs.h>
#define bufl (BCB **)0x4b2L /Systemvariable 'bufl'/
/* Definition DMD /
typedef struct
{ int d_roff[3];
int d_drive;
int d_fsiz;
int d_clsiz;
int d_clsizb;
int d_recsiz;
int d_numcl;
int d_lclsiz;
int d_mclsiz;
int d_lrecsiz;
int d_mrecsiz;
int d_lclsizb;
long d_fatfd; / korrekt: FD *d_fatfd /
long d_dummy;
long d_rdd; / korrekt: DD *d_rdd */
int d_flag;
} DMD;
/* Definition BCB */
typedef struct bctrl
{ struct bctrl *b_link;
int b_bufdrv;
int b_buftyp;
int b_bufrec;
int b_dirty;
DMD *b_dmd;
char *b_bufr;
} BCB;
/* globale Variable fĂŒr GEM */
extern int gl_apid; /* Applikations-ID /
GRECT desk; / Desktop-Ma_e /
int idum; / int-Dummy */
/* Daten einer Pufferliste anzeigen */'
show_bufl(beb)
register BCB *bcb;
{ int btype;
char stype[10];
char srec[10];
while (bcb)
{ btype = bcb->b_buftyp;
/* GEMDOS-Sektornummer aus BIOS-Sektornummer
berechnen falls möglich */
if (bcb->b_dmd)
/* nur wenn Laufwerk vorhanden */
sprintf(srec,"%d",bcb->b_dmd->d_roff[btype] + bcb->b_bufrec);
else
strcpy(srec,"-");
switch (btype)
{ case 0:
strcpy(stype,"FAT");
break;
case 1:
strcpy(stype,"DIR");
break;
case 2:
strcpy(stype,"DATA");
break;
default:
sprintf(stype,"%d",btype);
break;
}
printf
("%061x %04x %4s %4d %4s %04x %061x %061x \n",
bcb, bcb->b_bufdrv, stype, bcb->b_bufrec,srec, bcb->b_dirty,
bcb->b_dmd, bcb->b_bufr);
bcb = bcb->b_link;
}
}
do_work()
{ long stack;
graf_mouse(M_OFF); /* Maus ausschalten /
Cconws("\033H\033B\033B");
/ Cursor auf Beginn Zeile 2 /
printf
("BCB drv type rec sec dirty DMD bufr \n") ;
stack = Super (0L);
/ Supervisor-Mode notwendig /
show_bufl(bufl);
/ Pufferliste fĂŒr FAT ausgeben /
printf ("
\n"); / 80 Spaces /
show_bufl((bufl+1));
/ Pufferliste fĂŒr DIR/DATA ausgeben /
Super(stack);
/ zurĂŒck nach User-Mode /
graf_mouse (M_ON) ; / Maus wieder an /
Cnecin();
/ Anzeige bis Tastendruck halten /
/ Redraw des Desktop-Bildschirm ĂŒber GEM */
form_dial(FMD_FINISH,0,0,0,0,
desk.g_x,desk.g_y,desk.g_w,desk.g_h);
}
main()
{ int msg[8]; /* Message buffer */
int event;
int menu_id;
appl_init();
if (gl_apid >= 0)
/* 'appl_init' geglĂŒckt ? */
{ wind_get(0,WF_WORKXYWH,
&desk.g_x,sdesk.g_y,&desk.g_w,sdesk.g_h);
menu_id = menu_register(gl_apid, " Show buffer list");
/* dafĂŒr sorgen, da_ printf-Puffer-Malloc unter
AES geschieht */
printf("\n");
}
while (1) /* Endlos-Schleife */
{ event = evnt_multi(MU_MESAG,
0,0,0,
0,0,0,0,0,
0,0,0,0,0,
msg, 0,0, &idum, &idum, &idum, &idum, &idum, &idum);
if (event & MU_MESAG)
if (msg[0] == AC_OPEN)
/* nur AC_OPEN berĂŒcksichtigen */
if (msg[4] == menu_id)
/* eigenes Accessory? */
do_work(); /* los geht's */
} /* end of while */
}
</div>
Alex Esser