Halo Leuts,
ich will grade auf meinem Mikrocontrollerboard, welches einen LPC2468
hat, FAT32 implementieren, um von der SD-Karte Dateien zu lesen und zu
schreiben.
Ich weiss, das gab es schon 5646546533412 mal, und es gibt ungefähr
10000000 fertige Libs, aber ich will etwas lernen und es deshalb selber
machen.
Mein Problem ist jetzt nicht, dass es mir an Infos mangelt, sondern das
Gegenteil ist der Fall. Ich habe zu viel, teilweise leicht
widersprüchliche Angaben. fatgen103.doc von Microsoft hilft auch nicht
viel, die Datenstrukturen sind zwar schön beschrieben, aber es ist nicht
wirklich übersichtlich. Meine Frage: Wenn ich ein solches FS
implementieren will - wo fange ich am besten an? Sektoren lesen und
schreiben kann ich schon, daran soll es also nicht scheitern. Wie geht
es weiter? Ich scheitere schon nur an der Frage, wie ich jetzt ein File
lese. Ich weiss zwar, dass man irgendwie den Startcluster einer Datei
berechnen kann, und dann daraus die Sektornummer, aber irgendwie kriege
ich bis jetzt noch keinen funktionierenden Code zustande. Könnt ihr mir
ein bisschen auf die Sprünge helfen?
Was ich vor allem möchte:
Die FAT-Implementation soll möglichst ähnlich sein, wie die auf dem PC
(also was das File-Handling im Programm angeht mit fopen, fwrite und so
weiter). Ich will mehrere Dateien gleichzeitig öffnen, lesen und
schreiben. RAM ist genug da (128M).
@Ralf
>Ich weiss, das gab es schon 5646546533412 mal, und es gibt ungefähr>10000000 fertige Libs, aber ich will etwas lernen und es deshalb selber>machen.
Von den 1000000 Libs funktioniert nur eine handvoll wirlich gut.
>Ich habe zu viel, teilweise leicht>widersprüchliche Angaben. fatgen103.doc
Die Doku von MS ist ausreichend. Wenn du damit nicht klarkommst
ist das dein Problem.
>Wenn ich ein solches FS>implementieren will - wo fange ich am besten an?
Verstehen was ein MBR ist, was ein Bootsektor ist,
was eine Partition ist. Wie sieht eine FAT aus?
Was wird dort wie eingetragen? Wie groß sind die Cluster?
Wie groß ist der Eintrag in der FAT für einen Cluster?
>Ich scheitere schon nur an der Frage, wie ich jetzt ein File>lese.
Folge der Clusterchain!
>Ich will mehrere Dateien gleichzeitig öffnen,
Versuch es erstmal mit einer Datei.
Mehrere ist dann für die Fortgeschrittenen.
Ein eigenes FAT Dateisystem zu programmieren und zu
entwanzen dauert schon ein paar Monate/Jahre.
Also,
ich habe beim Googeln noch ein interessantes Dokument gefunden, wo das
ganze einigermassen leicht verständlich erklärt wird.
Sehe ich das richtig so:
Ich lese erst den Sektor 0 vom Medium, um den BPB (Bios Parameter Block)
zu kriegen. Mit den Infos, die dort drin stehen, kann ich dann
berechnen, wo die FAT beginnt, und wo die Directory Table sich befindet.
Richtig?
Um ein File zu suchen, muss ich im Directory Table-Bereich nach dem
gewünschten Namen suchen. Dort finde ich dann eine Struktur, wo
Dateinamen, Datum, Attribute sowie der Startcluster drin stehen.
Richtig?
Anhand der Nummer des Startclusters kann ich den Startsektor der Datei
berechnen. Wenn ich unter der Nummer dieses Startclusters in der FAT
nachschaue, dann sehe ich da, welches die Nummer des nächsten Clusters
ist. Wenn als nächster Cluster 0x0FFFFFFF eingetragen ist, dann ist die
Cluster Chain zu Ende.
Ist mein Vorgehen grundsätzlich richtig?
Nun habe ich nur noch ein Problem:
Ich wollte das mit der Directory Table als struct lösen. Sprich: ich
definiere ein struct, das genau einen Dir-Eintrag repräsentiert. Nun
habe ich aber festgestellt, dass die Dir-Einträge unterschiedlich lang
sein können :-( wie löst man dieses Problem?
Lange Dateinamen würde ich nämlich auch gerne Verwenden. 8.3 ist leider
unbrauchbar....
Grundsätzlich klingt das Vorgehen schon mal gut, aber...
> ich definiere ein struct, das genau einen Dir-Eintrag repräsentiert. Nun> habe ich aber festgestellt, dass die Dir-Einträge unterschiedlich lang> sein können :-( wie löst man dieses Problem?
...das ist Blödsinn, die Dir-Einträge sind alle exakt 32 Bytes lang. Es
gibt nur zwei unterschiedliche Typen.
> Lange Dateinamen würde ich nämlich auch gerne Verwenden. 8.3 ist leider> unbrauchbar....
Tja, lange Dateinamen ist ebenfalls für Fortgeschrittene. Wenn Dein FAT
mit 8.3 läuft und Du dann tatsächlich noch Lust haben solltest, lange
Dateinamen einzubauen, kannst Du das immer noch machen.
> ...das ist Blödsinn, die Dir-Einträge sind alle exakt 32 Bytes lang. Es> gibt nur zwei unterschiedliche Typen.
Also, das ist ja super.
Nun habe ich nur noch folgendes Problem:
Bei einem langen Dateinamen kommt erst der Dateiname, anschliessend der
eigentliche Directory Eintrag. Woher weiss ich denn nun im Voraus, ob es
sich bei dem folgenden File um eines mit oder ohne langen Namen handelt?
Wenn ich meinen FAT-Treiber so schreibe, dass er mit 8.3 funktioniert,
das Medium dann in Windows bearbeite und dadurch lange Dateinamen
entstehen - kann ich das Medium dann trotzdem noch lesen?
> Wenn ich meinen FAT-Treiber so schreibe, dass er mit 8.3 funktioniert,> das Medium dann in Windows bearbeite und dadurch lange Dateinamen> entstehen - kann ich das Medium dann trotzdem noch lesen?
Ja, natürlich. Nur die Dateinamen werden halt verkürzt dargestellt.
Diese Abwärtskompatibilität ist ja gerade der Grund, warum man sich bei
der Implementierung von LongFileName-Unterstützung so dermaßen einen
abbrechen muss...
> Woher weiss ich denn nun im Voraus, ob es> sich bei dem folgenden File um eines mit oder ohne langen Namen handelt?
Die Dir-Einträge, welche den langen Dateinamen enthalten sind nummeriert
und liegen IMMER direkt vor dem eigentlichen Dir-Eintrag der
entsprechenden Datei. Wenn Du also das Directory Eintrag für Eintrag von
oben nach unten durchgehst, stolperst Du also zwangsläufig zuerst über
die LongFileName-Einträge.
Ralf schrieb:
> Meine Frage: Wenn ich ein solches FS> implementieren will - wo fange ich am besten an?
Meine Startegie bei solchen Sachen war immer (auf diesen konkreten Fall
angewendet):
Lass dir von Windows eine SD-Karte neu formatieren, schreibe eine(!)
kleine Datei drauf und dann sieh in den Sektoren nach, was da alles wo
eingetragen wurde. Wenn du Doku dazu hast, sieh nach ob die Doku mit der
Realität übereinstimmt, bzw nimm Papier und Bleistift und werte die
Bytes mit der Hand aus und sieh zu, dass du gedanklich einen Filezugriff
durchspielst. Welches Byte kommt woher, welche Info kriege ich davon,
wie führt mich dieses Byte weiter. Solange bis ich bei den Datenbytes
des Files bin.
Gehts mit einer kleinen Datei, dann eine größere von Windows schreiben
lassen. Welche Bytes haben sich verändert, etc.
Hab ich verstanden, wie das mit einer Datei geht, überprüfe ich mein
Wissen, indem ich mehrere Dateien auf die Karte schreiben lasse. Gelingt
es mir immer noch die Datenbytes zu finden?
Was ist wenn eine Datei gelöscht wird, wie ändern sich dann die
Einträge?
Langer Rede, kurzer Sinn:
Die Strategie ist nicht, da jetzt tagelang Doku zu studieren und rein
anhand der Doku ein Programm zu bauen, sondern Doku studieren und anhand
der realen Daten (welche ein anderes Pgm geschrieben hat, in deinem Fall
Windows) überprüfen ob ich die Doku richtig verstanden habe. Dazu gehört
auch sich zu überlegen, wie ich Windows dazu bringen kann, das in der
Doku gelesene auch zu realisieren. Also: Wenn in der Doku steht, dieses
Bit zeigt das ReadOnly Flag an, dann lass ich mir von Windows eine
ReadOnly Datei schreiben und sehe nach. Hat sich ausser diesem einen Bit
noch etwas verändert? (Dazu ist es notwendig, dass man zb auch überlegt,
wie man reproduzierbare Sektordaten auf der Karte mit Windows erzeugen
kann).
Und erst dann geht es los und ich fange mit einer Implementierung an,
wobei auch das ein Wechselspiel ist. Ich implementiere nicht alles in
einem Rutsch, sondern teile das so auf, dass ich einzelne Schritte
testen kann. Testen, lange bevor alles fertig ist.
Ob das jetzt ein Filesystem ist, oder die Ansteuerung eines Plotters,
oder eines Spektrometers oder eines Terminals, oder .... Das Prinzip ist
immer das Gleiche: Ein Wechselspiel aus Doku lesen und sich das Gelesene
in Natura am Objekt ansehen.
Oh. Ganz wichtig. Am Anfang kleine Brötchen backen! Du hast schon
tausend Pläne, wie toll dein FS sein soll und welche Features es können
soll, etc. Fang erst mal klein an. Sieh zu dass du 8.3 lesen/schreiben
kannst. Es ist auch nicht ungewöhnlich bei derartigen Dingen, dass man 2
oder 3 Anläufe benötigt, bis die interne Programmstruktur so ist, dass
sie vernünftig ist. Also keine Angst davor, dass du nach Tagen
draufkommst, dass du ungeschickt angefangen hast. "Failure is not an
option" ist kein Hindernis für dich. Aus dem gescheiterten Versuch hast
du etwas sehr Wichtiges gelernt. Nämlich, dass es so nicht geht und vor
allen Dingen: warum es so nicht geht und in welche Probleme dieser
spezielle Ansatz führt. Und dieses Wissen ist unbezahlbar!
Hallo Leuts,
also ich habe mich nochmals hin gesetzt und vor allem fatgen103.pdf
genau studiert. Mittels Disk-Editor habe ich dann meinen
FAT32-formatierten Memory Stick auseinander genommen, und mittlerweile
bin ich so weit, dass ich anhand des BPB den Beginn der FAT bzw. der
Directory-Einträge bestimmen kann. Auch das durchlaufen der Cluster
chain geht. (bis jetzt habe ich das auf dem PC getestet, indem ich
einfach ein Image meines Memory Stick gemacht habe und dieses dann als
virtuelles Speichermedium verwende). Somit kann ich nun also eine Datei
lesen, wenn ich deren Anfangscluster kenne.
Das einzige, was mir jetzt noch Kopfzerbrechen bereitet, sind diese
Directory-Einträge. Mir ist nicht ganz klar, wie ich die finden soll -
klar, ich kenne den Startsektor, wo die Dir-Einträge liegen, aber damit
kenne ich ja erst die Position des 1. Eintrags. Die Position des
nächsten Eintrags könnte ich zwar berechnen, wenn ich weiss, ob es sich
beim 1. File um eines mit oder ohne langem Namen handelt. Das geht aber
nicht, da ich das im Voraus nicht wissen kann! Also, meine Frage
deshalb:
Ich kenne den aller ersten Directory-Eintrag. Wie finde ich den
nächsten?
Was ich schon weiss: wenn der Name eines Directory-Eintrags mit 0x00
oder 0xE5 beginnt, dann ist dies entweder ein gelöschtes File (0xE5)
oder es ist ein leerer Platz und es folgen keine weiteren Eintrage
(0x00).
Klar heisst es in fatgen103, dass FAT32 mit oder ohne lange Namen voll
kompatibel sind, und die langen Namen einfach ignoriert werden, wenn das
System sie nicht unterstützt. Aber irgendwo her muss das System ja
wissen, welche Daten es ignorieren soll und welche nicht ;-)
Das ist also das Einzige, womit ich noch Mühe habe. Ansonsten glaube
ich, habe ich FAT32 einigermassen verstanden....
>Ich kenne den aller ersten Directory-Eintrag. Wie finde ich den>nächsten?
Alle Directory Einträge (gelöschte Dateien, LFN Einträge) abklappern
bis du den nächsten gefunden hast.
Ralf schrieb:
> nächsten Eintrags könnte ich zwar berechnen, wenn ich weiss, ob es sich> beim 1. File um eines mit oder ohne langem Namen handelt. Das geht aber
Ob ein Directory-Eitrag ein LFN ist, kann am Attribut festgestellt
werden:
Die Bits ATTR_READ_ONLY, ATTR_HIDDEN, ATTR_SYSTEM und ATTR_VOLUME_ID
sind gesetzt. Ein 8.3-System ignoriert solche Einträge. Du solltest sie
erstmal ebenfalls ignorieren.
Steht in fatgen103.doc, Seite 26.
Grüße, Peter
Danke Peter, ich hab es auch grade gesehen!
Super Sache, so langsam wird einiges klarer.
Aber ich habe noch etwas weiteres festgestellt:
Nehmen wir an, ich habe einen leeren Datenträger. Den bearbeite ich
jetzt mit Windows (da mein eigener FAT-Treiber ja noch nicht läuft).
Jetzt erstelle ich also da drauf ein File - kein Problem, das File ist
sofort auffindbar, es wird nämlich durch den ersten Eintrag im Directory
Table repräsentiert. So, und jetzt erstelle ich einen Ordner. Der Ordner
wird korrekterweise im Directory Table abgebildet - es ist der Eintrag,
der gleich auf mein erstes File folgt. Schön und gut, aber dummerweise
müssen jetzt irgendwo noch die . und .. Einträge erstellt werden - und
die platziert Windows zu beginnn des nächsten Clusters. Das wäre ansich
auch kein Problem, aber wenn mein Directory Table jetzt wächst, stösst
es irgendwann an die Cluster-Grenze. Und dann verstehe ich nicht mehr,
was passiert - wo kommen denn jetzt neue Einträge rein, wenn das
Directory Table weiter wachsen soll? Irgendwie wird es fragmentiert
durch das Vorhandensein von Dateien und Ordnern :-(
>Und dann verstehe ich nicht mehr,>was passiert - wo kommen denn jetzt neue Einträge rein, wenn das>Directory Table weiter wachsen soll?
Du suchst dir einen freien Cluster und hängst ihn
ans Ende der Clusterchain des Directorys.
>Irgendwie wird es fragmentiert>durch das Vorhandensein von Dateien und Ordnern :-(
Das lässt sich nicht vermeiden.
holger,
okay klar soweit - neuen Cluster suchen und dann in der Cluster chain
einfügen.
Dummerweise kenne ich ja aber die Clusternummer des Directory Table
nicht - ich mache die Ausfindig über die Formel, die in fatgen103
angegeben ist. Irgendwie so: (Anzahl der FATs * Grösse der FAT) +
Reservierte Sektoren oder irgendwie etwas in der Art (habe den Code grad
nicht parat).
Ist das also nicht richtig so? Irgendwo her muss ich ja die
Clusternummer des Directory Table kennen, damit ich bestimmen kann, wo
ich den neuen Cluster einfügen muss....
@Simon
>Bist du dir sicher, dass du dem Projekt gewachsen bist?
Nun lass ihn mal machen, das fängt doch schon ganz gut an.
Ich bin gespannt wie weit er kommt! Vieleicht wächst hier
gerade ein neues selfmade FAT System.
Ralf schrieb:
> auch kein Problem, aber wenn mein Directory Table jetzt wächst, stösst> es irgendwann an die Cluster-Grenze. Und dann verstehe ich nicht mehr,> was passiert - wo kommen denn jetzt neue Einträge rein, wenn das> Directory Table weiter wachsen soll? Irgendwie wird es fragmentiert> durch das Vorhandensein von Dateien und Ordnern :-(
Die Root-Directory ist auf jeden Fall begrenzt. Meines Wissens ist die
Anzahl der dafür reservierten Sektoren bei FAT32 nicht angegeben.
Aber sowohl der Anfang der Root-Directory als auch der Beginn der Daten
ist angegeben, dazwischen liegt die Root, die Anzahl Sektoren muss man
wohl selbst ausrechnen.
Grüße, Peter
>Dummerweise kenne ich ja aber die Clusternummer des Directory Table>nicht
Doch, das steht bei einem Unterverzeichnis im Directory Eintrag
für das Unterverzeichnis. Für das Rootdirectory ist es ein fester
Wert. Schaun wir mal ob du rausfindest welcher das ist ;)
>Die Root-Directory ist auf jeden Fall begrenzt. Meines Wissens ist die>Anzahl der dafür reservierten Sektoren bei FAT32 nicht angegeben.
Bei FAT32 ist das Rootdirectory in der FAT eingetragen und kann
beliebig vergrößert werden. Nur bei FAT12 und FAT16 ist die Größe
des Rootdirectory fix.
Peter,
so wie ich das verstanden habe, ist bei FAT32 die Anzahl der Einträge im
Root Directory nicht begrenzt.
Bei FAT12 und 16 war sie es, ich glaube auf 512 Einträge.
Simon:
Ja, ich denke schon dass ich dem Projekt gewachsen bin. Ich bin nicht
dumm, meine Stärke liegt halt eher auf der Hardware-Seite. Ausserdem
beschäftige ich mich auch erst seit etwa gestern ernsthaft mit dem
Thema, da finde ich es normal, dass man zu beginn einige Fragen hat bzw.
einiges unklar ist. Dass du FAT32 von Beginn an gleich durchschaut hast,
und es für dich ein leichtes war, das zu Programmieren, glaube ich gern,
aber ich für meinen Teil tue mir damit etwas schwerer. Zumal die Doku
von MS zwar ausreichend, aber nicht wirklich toll ist; was die Sache
nicht unbedingt vereinfacht.
holger:
ich hoffe sehr, dass ich mein Programm irgendwann mal zum Laufen kriege.
Man findet zwar im Netz zahllose Libraries für FAT, aber keine ist so
richtig toll (für meinen Geschmack).
Ich will was bauen, was vor allem auf Performance ausgelegt ist (da es
auf einem ARM laufen soll, spare ich auch nicht unbedingt an RAM oder
dergleichen). Es soll möglichst schnell sein, und soweit es geht
PC-Kompatibel (was glaube ich nicht bei allen erhältlichen FAT Libs zu
100% der Fall ist).
Wenn ihr wollt kann ich ja dann, wenn ich fertig bin, den Code hier
reinstellen (als Dankeschön sozusagen für eure Hilfe ;-)).
holger,
> Doch, das steht bei einem Unterverzeichnis im Directory Eintrag> für das Unterverzeichnis. Für das Rootdirectory ist es ein fester> Wert. Schaun wir mal ob du rausfindest welcher das ist ;)
Meinst du Cluster Nr. 2? Die FAT-Einträge 0 und 1 sind ja reserviert.
Dann nehme ich mal an, beginnt das Root-Directory bei Cluster Nr. 2.
holger schrieb:
> @Simon>>>Bist du dir sicher, dass du dem Projekt gewachsen bist?>> Nun lass ihn mal machen, das fängt doch schon ganz gut an.> Ich bin gespannt wie weit er kommt! Vieleicht wächst hier> gerade ein neues selfmade FAT System.
;) Ja vielleicht. Ich meine nur, so oft wie er hier nachfragt und was
nicht versteht. Erster Schritt wäre vielleicht: Selber herausfinden und
das Posten im Forum als letzten Ausweg sehen, so mache ich das nämlich
immer. Wo bleibt die Herausforderung. Es gibt sooo viel Infos zu FAT im
Internet und auch in diesem Forum.
Notfalls guckt man mal in andere Sourcen rein und lässt sich
inspirieren.
Stattdessen herrscht hier eher eine Schüler-Nachhilfelehrer Situation.
>Stattdessen herrscht hier eher eine Schüler-Nachhilfelehrer Situation.
Ich finde er hat sich schon ganz gut informiert
und ein wenig Nachhilfe verdient ;)
Simon,
ich werde versuchen mich zu Bessern.
Du sagst, man findet viele Infos zu FAT. Das ist richtig. Und genau das
ist mein Problem! Für meinen Geschmack sind es eben zu viele Infos. Ich
weiss gar nicht, wo ich beginnen soll! Oder wusste es bis jetzt nicht.
Mittlerweile habe ich eine ungefähre Vorstellung davon, wie man von
einem Datenträger liest, die Cluster ausfindig macht usw. Mein Problem
ist, dass die fatgen103 Doku zwar alles beinhaltet, was man braucht,
aber es werden halt nur die Strukturen beschrieben - welche Felder sie
beinhalten, wie breit diese sind und so weiter. Aber was ich jetzt mit
einer FSInfoStruct anfangen soll, das steht nicht drin, das muss man
irgendwie rein interpretieren - und das kann ich nicht so gut.
holger,
Danke! das passt. In meinem Fall ist BPB_RootClus 2, wie wohl in den
meisten Fällen.
Das bedeutet also: Um mein Medium nach Dateien und Ordnern zu
durchsuchen, muss ich BPB_RootClus auswerten, um den Anfang der DirTable
zu bestimmen. In der FAT kann ich nachschauen, in welchen Clustern die
DirTable sich befindet. Und innerhalb dieser DirTable ist es dann - mehr
oder weniger - ein Kindespiel, die Files bzw. Ordner heraus zu
pfriemeln.
Danke!
Ich habe bei mir das gleich so implementiert. Ich lese jetzt einen
Cluster der DirTable, lege den im Memory ab und betrachte das nachher
als Array von DirEntry-Structs (und eine DirEntry-Struct sieht natürlich
genau so aus wie in fatgen103 beschrieben).
Denkst du, das ist ein sinnvolles Vorgehen, um schnell arbeiten zu
können? Es ist natürlich zum Programmieren sehr einfach; der Code wird
leichter lesbar, als wenn man da solche Akrobatik mit x Pointern macht
und mit den Offsets irgendwas herum rechnet, wie man das in vielen
FAT-implementationen sieht.
Ich denke mal, auch bei PCs wird man niemals die komplette DirTable oder
die komplette FAT im Memory haben. Oder täusche ich mich da? Wie machen
die das, dass der File-Zugriff möglichst einfach und schnell wird?
Wichtig ist: ich will mich nicht mit irgendwelchen Pointern und
dergleichen abplagen. Das ist unübersichtlich, und wie ich finde,
irgendwie Murks. Genau dazu gibt es doch stucts ;-) Mittlerweile habe
ich einen ungefähren Durchblick. Lange Dateinamen ist mittlerweile auch
klar.
Dieses Forum hier ist echt geil :-) Aus der Doku, die man im Internet
findet, hätte ich solches Zeug niemals raus lesen können!
>Ich lese jetzt einen Cluster der DirTable,
Du scheinst aber eine Menge RM zu haben.
So ein Cluster kann schon mal 64Kb haben.
>als Array von DirEntry-Structs
Schön aufpassen das das struct "packed" ist.
Bei Big Endian Maschinen bekommst du trotzdem Ärger.
>Ich denke mal, auch bei PCs wird man niemals die komplette DirTable oder>die komplette FAT im Memory haben. Oder täusche ich mich da? Wie machen>die das, dass der File-Zugriff möglichst einfach und schnell wird?
Die komplette FAT wird nicht im Speicher gehalten.
Viel zu groß. Ein kleiner Teil beschleunigt auch schon.
Ein Sektor z.B. Wenn du Platz für einen Cluster hast, umso besser.
holger,
Also mein Rechner ist ein Little Endian (ein LPC2000). Das passt also
perfekt :-)
Und ja, ich habe eine Menge RAM. Dank dem SDRAM-Interface kann ich 256M
benutzen; im Moment sind zwar nur 64 auf meinem Bastel-Board, aber
besser als gar nichts ;-) Und damit es noch ein bisschen besser wird,
ist das ganze 32 Bit breit angebunden.
Ich habe mir dazu auch einen eigenen Memory-Manager geschrieben (ich
will die ganze Software da drauf selber machen), sodass ich mittels
malloc und free den grossen Speicher schön bequem verwalten kann. Ich
denke, ich werde das so lösen:
Ich bestimme, wie viele Sektoren ein Cluster hat, und reserviere dann so
viel Memory. In dieses kommt dann ein Teil der FAT; in einen anderen
(ebenso grossen Bereich) kommen die DirTables rein. Damit ich mich in
der FAT zurecht finde, nehme ich eine Variable, die mir den Offset
angibt, also welcher Teil der FAT sich im Memory befindet. Bevor ich
dann einen bestimmten Cluster suchen kann, muss ich prüfen, ob er in dem
bereits im Memory befindlichen FAT-Bereich liegt; wenn nicht wird
berechnet, welche Sektoren nachgeladen werden müssen.
Ich denke das sollte so funktionieren, nicht? Klingt das plausibel und
einigermassen performant? Ansonsten muss ich mir überlegen, wie man es
noch schneller machen könnte. Der Rechner wird wohl nicht das Problem
sein, der kann seine Daten meinetwegen per DMA aus dem Medium holen.
Ausserdem läuft er mit 72 MHz, wovon ich mir eine einigermassen gute
Leistung verspreche. Mal schauen :-)
>Ich denke das sollte so funktionieren, nicht? Klingt das plausibel und>einigermassen performant?
Perfekt! Wie gesagt, ich bin gespannt was dabei raus kommt.
Hallo,
um den FAT Teil zu puffern musst du nicht rausfinden wieviele Sektoren
einen Cluster bilden, da nur der Daten Bereich in Clustern zusammen
gefasst wird...
Interessehalber mach dir mal ein paar Daten klar.
Wenn du einen FAT Sektor zwischen pufferst, und das Dateisystem mit z.B.
8 Sektoren/Cluster Formatiert ist kannst du 512*8*128 = 524288 Bytes am
Stück lesen, WENN alle FAT Einträge in dem gepufferten Sektor zusammen
gehören. Das ist schonmal nicht schlecht :)
Sagen wir mal du spendierst einen Puffer für 128 FAT Einträge, also
Cluster Nummern, dann ist die Optimale Vorgehensweise:
1. Annahme Idealfall (FAT unfragmentiert):
Merk dir den 1. Cluster und gehe durch die FAT-Chain bis du beim letzten
Cluster angekommen bist, so kannst du mit 8 Gespeicherten Bytes (Start
und End Cluster) Dateien mit beliebiger Größe lesen.
2. Annahme nicht Idealfall (FAT fragmentiert):
Wieder 1. Cluster merken, jetzt wird es ungemütlicher, weil die FAT
Kette nicht am Stück ist sondern in einzelne kleine Ketten aufgeteilt
ist. Jetzt muss man sich immer Paare von Anfang und Ende merken. Da ist
jetzt nur die Frage, wieviele dieser Kettenpaare man sich merken möchte
:)
Hoffe das ist verständlich^^
Ach ja, diese Überlegungen gelten so nur für lesenden Zugriff..
holger,
Okay, super Sache. Ich habe das gleich so implementiert in der Software;
heute Abend werde ich dann wieder Zeit zum Basteln haben und testen, ob
das so gut funktioniert.
Nur eine Unschönheit habe ich festgestellt: Wenn bereits einige
FAT-Einträge im Puffer liegen, und ich Zwecks Erstellen einer neuen
Datei nach freien Clustern suche, dann muss ich erst den Puffer leeren,
dann die FAT von Beginn an neu laden und von vorne alle Einträge
abklappern, bis ich einen freien Cluster gefunden habe. Klar könnte ich
auch gleich den Puffer durchsuchen, der im Memory liegt, aber so würde
ich vielleicht gewisse FAT-Bereiche gar nie nutzen... das selbe beim
Directory Puffer.
Was natürlich auch doof ist - lese ich eine Datei, und merke, dass der
nächste Cluster nicht mehr in meinem Puffer ist, dann muss ich erst
prüfen, ob der Inhalt des Puffers verändert wurde, wenn ja diesen auf
dem Medium speichern, und dann den nächsten Bereich in den Puffer laden.
Das ist etwas hässlich, und sicher auch langsam, aber mir fällt keine
Lösung ein, wie sich das vermeiden liesse. Ich nehme an, auf PCs wird
das genauso gehandhabt - einen Teil der FAT in den Puffer laden, und
wenn man Daten benötigt, die nicht im Puffer vorhanden sind den Puffer
speichern und neu laden.
Oder hast du dazu auch irgend eine kluge Idee?
zerrome,
Ich weiss dass nur der Datenbereich in Clustern unterteilt ist. Der
Einfachheit halber will ich aber die Puffer sowohl für die FAT als auch
für die Dir Table so gross wie ein einzelner Cluster machen, das hat den
Vorteil, dass die Funktion zum Lesen und Schreiben der Puffer nur auf
eine bestimmte Grösse eingestellt werden muss. Ausserdem verspreche ich
mir dadurch einen Geschwindigkeitsvorteil - mehr FAT-Einträge im Puffer
= grössere Geschwindigkeit.
Dass ich 524288 Bytes lesen kann mit einem solchen Puffer ist mir klar;
ich sehe aber ein Problem, wenn die FAT fragmentiert wird. Dann kann ich
immer weniger Bytes auf einmal lesen, was nicht so dolle ist.... Aber
ich werde sicher prüfen, wie viel FAT sinnvoll zu puffern ist.
Ralf schrieb:
> Was natürlich auch doof ist - lese ich eine Datei, und merke, dass> der nächste Cluster nicht mehr in meinem Puffer ist, dann muss ich> erst prüfen, ob der Inhalt des Puffers verändert wurde, wenn ja> diesen auf dem Medium speichern, und dann den nächsten Bereich> in den Puffer laden.> Das ist etwas hässlich, und sicher auch langsam, aber mir fällt> keine Lösung ein, wie sich das vermeiden liesse. Ich nehme an,> auf PCs wird das genauso gehandhabt - einen Teil der FAT in den> Puffer laden, und wenn man Daten benötigt, die nicht im Puffer> vorhanden sind den Puffer speichern und neu laden.> Oder hast du dazu auch irgend eine kluge Idee?
Ganz genau so wird das gemacht.
Sieh zu, dass du diese Cache-Logik (denn genau darum handelt es sich),
in ein paar Funktionen auslagerst. Für die Codeteile, die darüber
liegen, sieht es so aus, als ob alles im Speicher liegt. Die braucht
nicht zu interessieren, dass du da im Hintergrund
speichern/lesen/verwerfen musst.
Der Knackpunkt am ganze ist, dass du im Code nicht Wildwuchs betreibst
und jeder dahergelaufene Code überall einfach zugreift, wie er lustig
ist, sondern dass das alles geordnet geht. Du brauchst den Dir-Eintrag
Nr.5? Dann rufe die Funktion auf, die dafür zuständig ist, einen
bestimmten Eintrag zu besorgen. Selbst dann wenn du bereits einen
Pointer auf den Dir Eintrag Nr 0 hast und dir die Position selber
ausrechnen könntest. Denn irgendwann benötigst du dann den Eintrag 2087.
Kein Problem: Die Funktion weiß, wie man das macht. Aber der Code der
nur den Pointer auf den 0-ten Eintrag hat nicht. Und der soll das auch
gar nicht wissen.
Module bauen!
Und diese ganzen speziellen Lade/Nachlade/Speichern - Cache-Logiken in
diese Module einbauen.
Hallo Karl heinz,
so ähnlich habe ich mir das vorgestellt.
ICh habe eine Funktion GetNextCluster - die liest aus der FAT heraus,
welches der nächste Cluster ist. Dazu prüft sie, ob der gewünschte
Cluster bereits im Puffer is, ansonsten lädt sie den Puffer neu und
speichert den alten, falls geändert.
Ganz ähnlich funktioniert GetNextFreeCluster. Die gibt einfach die
Nummer des nächsten freien Clusters zurück. Da weiss ich jetzt noch
nicht, in wie weit es Sinn macht, die FAT von Offset 0 an nach freien
Clustern zu durchsuchen, oder einfach den ersten freien Cluster im
aktuellen Puffer zu suchen. Letzteres wäre sicherlich schneller, aber in
Bezug auf die Fragmentierung suboptimal.
Module habe ich in dem Sinne einfach gemacht, in dem ich ein Modul
fat.c/fat.h habe, wo das ganze Zeug mit Sektoren und Clustern und
Puffern gehandhabt wird. Im Modul file.c/file.h werden dann Funktionen
drin sein, die mittels fat.c/fat.h Dateien bearbeiten (also öffnen mit
fopen und schreiben mit fwrite und dergleichen).
Ein eigenes Cache-Modul gibt es nicht. Meinst du, das wäre erforderlich
oder sinnvoll?
>die FAT von Offset 0 an nach freien>Clustern zu durchsuchen, oder einfach den ersten freien Cluster im>aktuellen Puffer zu suchen. Letzteres wäre sicherlich schneller, aber in>Bezug auf die Fragmentierung suboptimal.
Fragmentierung kann dir in beiden Fällen passieren. Nimm einfach
erst den Puffer. Es ist (gefühlt) wahrscheinlicher das du eher
freie Cluster findest wenn du von deinem aktuellen Puffer ausgehst.
Am Anfang der FAT sind doch eher belegte Cluster zu erwarten.
Vom Puffer aus bis ans Ende der FAT suchen. Falls bis zum Ende
nix gefunden wird vom Anfang bis zum aktuellen Puffer. Wieder
nix gefunden: Disk full.
Oder schreib dir der Einfachheit halber erst einen Code
der tatsächlich immer bei 0 anfängt. Optimieren machst du später.
also, ich werde das jetzt aus Performance-Gründen wie folgt machen:
Wenn ich einen neuen Cluster brauche, dann wird in dem Teil der FAT
gesucht, der aktuell gepuffert ist. Sind dort keine neuen Cluster
vorhanden, dann muss der nächste Part der FAT geladen werden usw.
Ich habe bis jetzt noch keine speziellen Dateifunktionen eingebaut, aber
offensichtlich scheint meine bisherige Software zu funktionieren. Die
ist durchtbar dumm - sie kann nichts anderes, als einen FAT-Datenträger
mounten, BPB auslesen, die entsprechenden Puffergrössen berechnen und
die gewünschten Datenbereiche (also FAT und Directory Table) in
ebendiese Puffer laden.
Ausserdem kann man mittels GetNextCluster die Cluster chain durchlaufen,
bzw. man kann mit GetNextFreeCluster nach neuen, leeren Clustern suchen.
Beides klappt schon, und die Puffer werden korrekt geladen und wieder
auf das Speichermedium geschrieben.
Das heisst: jetzt müssen nur noch die Directory Table Einträge
interpretiert werden, dann kann ich erst Dateien und Ordner lesen. Aber
es ist tatsächlich nicht so schwierig, wie ich es mir Vorgestellt habe!
Super Sache.
Noch eine Frage:
Angenommen, ich will eine 72 MB grosse Datei bearbeiten (in Wirklichkeit
habe ich keine so grossen Dateien, das dient nur als Beispiel). Ich habe
aber nur 64 MB RAM. Also kann ich nicht die komplette Datei im Memory
halten, sie dort bearbeiten und anschliessend zurück schreiben.
Wie wird das denn professionell gemacht? Angenommen, ich füge an
Position 23 ein Byte ein. Dann muss ich ja den ganzen Datei-Inhalt um 1
Byte nach 'hinten' verschieben.... das beisst sich irgendwie mit der
Memory-Grösse. Also bin ich grade am überlegen, wie viel Memory ich
sinnvoll reservieren soll, wenn der Benutzer eine Datei öffnet. Wieder
ein Cluster? Das ergäbe Sinn, aber ist auch doof, wenn die Datei > 1
Cluster ist. Andererseits habe ich nicht beliebig viel Speicher....
>Wie wird das denn professionell gemacht? Angenommen, ich füge an>Position 23 ein Byte ein. Dann muss ich ja den ganzen Datei-Inhalt um 1>Byte nach 'hinten' verschieben.... das beisst sich irgendwie mit der>Memory-Grösse.
Temporäre Datei anlegen. Bis Byte 22 Originaldaten reinkopieren.
Byte 23 anfügen. Rest von Originaldaten dranhängen. Original
löschen, Temp Datei umbenennen.
holger,
Das mit der temporären Datei ergibt Sinn. Auf die Idee bin ich noch gar
nicht gekommen! Das erklärt auch, warum Windows immer diesen doofen
TEMP-Ordner hat, wo allerlei Mist drin landet ;)
Also, mittlerweile sind mehr oder weniger alle meine Fragen geklärt. Das
FS läuft zwar noch nicht, aber das wird schon. Hoffentlich ;) Bis jetzt
schaut es aber gut aus. Einziges Problem bereitet mir noch die
kompatibilität - eigentlich wollte ich ein einziges Programm, welches
sowohl FAT32 als auch FAT16 und FAT12 kann. Aber das geht offenbar nicht
mit meiner Vorgehensweise, da werde ich mir wohl noch was einfallen
lassen müssen....
>Das mit der temporären Datei ergibt Sinn.
Das war nur eine schnelle Lösung.
>eigentlich wollte ich ein einziges Programm, welches>sowohl FAT32 als auch FAT16 und FAT12 kann.
Vergiss FAT12. FAT16 ist auch bald tot weil es keine
kleinen Karten (<4GB) mehr zu kaufen gibt. FAT32 wird uns
noch ein wenig begleiten. Die Frage ist wie lange?
Naja, so lange wohl, wie es diese Karten gibt. Denn NTFS lässt sich da
nicht s einfach einsetzen, nicht ohne Grund kann man Karten damit nicht
formatieren.
Oder denkst du, es wird ma ein neues FS kommen, extra für Karten?
Nee, ich glaube FAT32 wird schon noch ne Weile leben. Man könnte
allerdings mal darüber nachdenken, die Struktur ein bisschen zu
modernisieren. Sprich: Alles nur noch mit langen Dateinamen, dafür eine
einfachere Struktur für die Dir-Einträge z.B. Halt solche alten Zöpfe
vergessen; ebenso, wie die Clusternummern nur bis 0x0fffffff gehen
dürfen.
>Oder denkst du, es wird ma ein neues FS kommen, extra für Karten?
Wer weiss ;) Aber vergiss FAT12. FAT16 einzubinden ist nicht schwer
wenn du es unbedingt brauchst.
>Nee, ich glaube FAT32 wird schon noch ne Weile leben. Man könnte>allerdings mal darüber nachdenken, die Struktur ein bisschen zu>modernisieren. Sprich: Alles nur noch mit langen Dateinamen, dafür eine>einfachere Struktur für die Dir-Einträge z.B. Halt solche alten Zöpfe>vergessen; ebenso, wie die Clusternummern nur bis 0x0fffffff gehen>dürfen.
Ich hab mal irgendwo was über ExtFAT32? gelesen.
64Bit für die Dateilänge möglich, also auch Dateien mit mehr
als 4GB auf FAT. Theoretisch geht FAT32 bis 8TB ;)
Das ist nicht das einzige Problem, es werden auch 32 Bit für die
Dateilänge im 32 Byte Dateieintrag benutzt... und da ist nicht mehr
Platz.
Eigentlich werden Cluster Nummern in der FAT mit 32 Bit gespeichert.
Jetzt setzen sich Cluster aber aus mehreren Sektoren zusammen....Bei 8
Sektoren/Cluster würde das 8*512*2^32 geben also 1,7 * 10^13 Bytes, was
definitiv nicht mehr in 32 Bit Dateigröße passt, es gibt aber
extFat,welches aber noch nicht ganz offen ist.
http://de.wikipedia.org/wiki/File_Allocation_Table#exFAT
Hallo,
falls es jemanden interessiert:
ich hab jetzt meine eigene FAT32-Implementierung am Laufen!
Inclusive langer Dateinamen etc.
Man kann Dateien öffnen, indem man entweder den langen oder den kurzen
Namen verwendet. Ausserdem ist mein Code voll PC-kompatibel, d.h. ich
kann alles lesen, was mittels PC auf die Karte gepackt wurde.
Der Speicherverbrauch hängt vom Dateisystem ab: es werden immer ganze
Cluster gepuffert; ich habe dazu einen Memory-Manager programmiert, der
ähnlich wie malloc funktioniert und mit dem sich auf dem Heap dynamisch
Memory allozieren und freigeben lässt. Beim Initialisieren werden dann
die ersten paar Einträge der FAT gepuffert sowie ein paar Directory
Entries; Danach kann man bereits auf das Dateisystem zugreifen. Beliebig
viele Dateien gleichzeitig offen zu haben ist möglich.
Im Moment bin ich noch dabei, eine komplette File-API zu implementieren;
momentan lassen sich Dateien nur Byteweise lesen, aber es wird natürlich
alle anderen Funktionen auch geben, wie sie in stdio.h auch existieren.
Soweit der Stand.
Wie schnell der Code ist, habe ich noch nicht genau gemessen, aber ich
kann sagen dass bis jetzt alles wirklich sehr flüssig läuft.
Bei interesse lade ich den Kram dann mal hoch, wenn ich fertig bin.
Gruss