Hallo Leute, ich möchte zur Verwendung in einem RTOS ein Filesystem programmieren, damit ich auf SD-Karten und MMC-Karten zugreifen kann. Zum Datenaustausch mit dem PC wird es wohl unumgänglich sein, FAT zu verwenden :-( Ich hab nun also mal begonnen mit der Implementierung; vorerst mache ich alles im MS Visual Studio, weil da schon eine funktionierende Umgebung vorhanden ist; die SD-Karte fake ich mit einem Image, das ich mittels Hexeditor gemacht habe. Ich frage mich jetzt, ob meine Architektur, die ich mir überlegt habe, klug ist. Ich mache es wie folgt: es gibt 2 interne Buffer, einen für die FAT, und einen für die Directory Entries. Wenn ich ein File suche, dann lade ich den Root Cluster, oder halt das Root Directory, in den Directory Buffer, um es dort zu traversieren; ausserdem gibt es ein "dirty"-Flag, das gesetzt wird, wenn irgendwas am Buffer modifiziert wurde (Datei erstellt, gelöscht etc.). Jedes mal, wenn der Buffer neu geladen werden muss, weil ein File gesucht wird oder dergleichen, wird das dirty-Bit dann abgefragt und der Buffer ggf. noch auf die Karte geschrieben. Beim FAT-Buffer mache ich es genauso. Jetzt ist die Sache die, ich möchte das ganze reentrant machen, damit in meiner OS-Umgebung ggf. auch mehrere Tasks ein File öffnen können. Könnt ihr mir ein paar Tipps geben, wie man das richtig macht? Meine Buffer habe ich der Einfachheit halber 512 Bytes gross gewählt, damit passt jeweils exakt 1 Sektor rein. Zudem habe ich um Files öffnen/lesen/schreiben zu können ähnich wie in der C-Library eine struct file_t definiert, dort drin ist nochmal ein 512 Bytes Buffer für jedes offene File; beim öffnen wird dann dieser Buffer gefüllt, bzw. wenn man fwrite aufruft wird immer in diesen Buffer geschrieben, und erst wenn er voll ist/fflush/fclose aufgerufen wird, wird er geschrieben. Darf man das so machen, oder verschwende ich zu viel Memory? Eleganter Ansatz? was könnte ich besser machen? Ich möchte das auf einem ARM Prozessor einsetzen; prinzipiell steht also schon einigermassen viel RAM zur Verfügung, aber wenn ich mir die anderen Implementierungen anschaue, z.B. von Holger Klabunde, der mit 100 Bytes RAM auskommt, dann bin ich schon recht verschwenderisch :-) Wäre mal gespannt auf eure Statements. Ach ja: ich weiss, es gibt auch CHaNs FatFS, das toll funktioniert; ich möchte es aber wirklich selber machen, wegen des Lerneffekts :-) Ich komme auch gut voran, möchte aber mal wissen, was ihr von meinem Ansatz haltet. Gruss
Um Reentranz zu erreichen brauchst Du für jeden Vorgang eigene Puffer. Andernfalls müsstest Du bei jedem einzelnen Zugriff den Puffer immer wieder umladen. Wenn Du beispielsweise drei Dateien offen hast und drei Threads schreiben nun jeweils ein Byte in die Datei, dann müsstest Du drei mal den aktuellen Block in den Puffer laden, das eine Byte ändern und dann den Puffer wieder auf die Karte schreiben. Denn einzelne Bytes kann man bei SD Karten ja nicht lesen/schreiben. Die Performance dürfte entsprechend lahm sein.
>aber wenn ich mir die >anderen Implementierungen anschaue, z.B. von Holger Klabunde, der mit >100 Bytes RAM auskommt, dann bin ich schon recht verschwenderisch :-) Das ist ein Sonderfall den du in deine Betrachtungen nicht einbeziehen kannst. Dort wird von Vorraussetzungen ausgegangen die du nicht einhalten kannst und auch nicht willst. >Um Reentranz zu erreichen brauchst Du für jeden Vorgang eigene Puffer. Und eine Absicherung das beim lesen/schreiben eines Sektors kein anderer Task Zugriff auf die Karte bekommt. Paralleler Zugriff ist nicht möglich.
>>Um Reentranz zu erreichen brauchst Du für jeden Vorgang eigene Puffer. > >Und eine Absicherung das beim lesen/schreiben eines Sektors kein anderer >Task Zugriff auf die Karte bekommt. Paralleler Zugriff ist nicht >möglich. Nachtrag: Was dann bedeuten kann das ein Task bis zu 500ms warten muss bis er auf die Karte zugreifen kann. SD Karten sind groß, aber nicht unbedingt schnell.
Hallo, danke für eure Posts. Dass ich, wegen der Reentranz, eigene Buffer brauche, ist mir klar. Das heisst also: ich muss in der Funktion, die die FAT traversiert, jeweils 512 Bytes für die FAT und nochmal 512 Bytes für die Directory Entries reservieren, plus eventuell nochmal 512 Bytes für die geöffnete Datei. Korrekt? Das liegt dann alles auf dem Stack, sprich: 1.5 KB sind erst mal schon allein für die Verwaltung reserviert. Kommen dann u.U. nochmal 256 Bytes für die Langen Dateinamen hinzu. Darf man das so machen? Mein bisheriger Ansatz war, einen einzigen, globalen Buffer für die FAT vorzusehen; auf diesen wird mittels Semaphor zugegruffen. Wie machen es die Profis?
Ich würde mich nicht wundern, wenn im professionellen Umfeld, so etwas in eigene Instanz ausgelagert wird. Dann erübrigen sich die meisten Probleme mit der Reentranz. Ich kann mir zumindest nicht vorstellen, das eine Datei- und Belegtverwaltung funktionieren kann, bei der zwei oder mehr Teilnehmer Zugriff haben, die in letzter Konsequenz nichts voneinander wissen.
>Wie machen es die Profis?
Sie meiden die kleinen Mistdinger.
Schon mal über eine Notstromversorgung nachgedacht
damit deine ganzen Buffer auch noch geschrieben werden können
wenn der Strom ausfällt?
Hallo, meiner Ansicht nach ist Reentrance hier der falsche Begriff, das File-System muss zunächst mal Transaktionen ausführen, d.h. wenn es Lesen oder Schreiben soll, dann muss es diese Aktion ganz durchführen oder alles angefangene rückgängig machen, ganz egal was sonst passiert. Reentrance geht an dieser Stelle nicht. Andere Tasks müssen bis zum Abschluss der Transaktion warten, Realtime hin oder her. Man muss also drauf achten, dass Dateioperationen eine Task nicht blockieren können! In Windows gibt es dafür non-blocking Aufrufe, die fordern die Aktion nur an, bei Abschluss wird die Task benachrichtigt. Natürlich brauchst du einen Sector Buffer für eine Task. Wozu eine Task aber einen eigenen Directory Buffer haben soll, erschliesst sich mir so einfach nicht, im Gegenteil, die Verwaltung des Directories darf nur in einer Instanz geschehen. Aber das ist nur so der erste Eindruck, File Systeme müssen sorgfältig durchdacht sein. Gruss Reinhard
Hallo, ich glaube ihr habt mich ein wenig missverstanden :-) es gibt in meinem FAT-Modul zwei globale Variablen, einmal ein Buffer für die FAT sowie ein Buffer für irgendwelche beliebigen Directory Entries. Beim Initialisieren wird erstmal der 1. Teil der FAT in den FAT-Buffer gelesen, und das Root Directory wird in den Directory Buffer gelesen. Wenn jetzt irgend ein Task ein File öffnen möchte, muss man natürlich immer beim Root Directory zu suchen beginnen; beispielsweise möchte ich ein File /ordner/file.ext öffnen, dann muss ich erst Sektor für Sektor das Root directory in den Directory Buffer laden, diesen durchsuchen nach "ordner", und wenn gefunden, dessen Startcluster dann in den Directory Buffer laden und dann nach "file.ext" suchen. Das Problem, das sich mir aber stellt ist, dass, weil der Directory und der FAT Buffer global sind, in der Zwischenzeit ein anderer Task auch versuchen könnte, ein File zu öffnen, und mir die Buffer überschreibt. Aber so wie ich den Tenor hier verstehe, kann man das gar nicht anders lösen, als die Buffer mit Semaphoren oder Mutexen zu schützen, auf die der jeweilige Task dann halt warten muss. Andere Frage: Der Einfachheit halber möchte ich meine Buffer nicht mit malloc() alloziieren; sprich, sie haben demnach auch eine feste Grösse. Ich finde, 512 Bytes, also 1 Sektor, ist eine gute Grösse. Die Frage ist allerdings: darf ich davon ausgehen, dass käufliche SD/MMC-Karten 512 Bytes Sektorgrösse haben? Wie sieht es mit SDHC aus? Ich habe bereits danach gesucht, aber ich bin mir nicht sicher; man findet widersprüchliche Angaben; die einen sagen, es sei immer 512 Bytes, andere sagen, man müsse im CID-Register nachschauen, dort würde die Sektorgrösse stehen. Das Problem wäre halt, dass wenn die Sektoren andere Grössen hätte, ich meine Buffer nicht mehr statisch alloziieren könnte und zudem (wenn sie grösser als 512 Bytes sind) mehr Speicher "verschwendet" würde.
igor schrieb: > Das Problem wäre halt, dass wenn die Sektoren andere Grössen hätte, ich > meine Buffer nicht mehr statisch alloziieren könnte Buffer = Sektor ist kein Naturgesetz. Schon beim Urvater CP/M war der BS-Sektor 128 Byte, auf den späteren Disketten aber 512, dafür gab es das Blocking/Deblocking. Das macht die Software natürlich komplizierter, dafür kann man im BS mit einer festen Grösse arbeiten. Abgesehen davon kann man den grössten vorkommenden Sektor allozieren, das ist ja eh der worst case, dan man berücksichtigen muss. Gruss Reinhard
igor schrieb: > Das Problem, das > sich mir aber stellt ist, dass, weil der Directory und der FAT Buffer > global sind, in der Zwischenzeit ein anderer Task auch versuchen könnte, > ein File zu öffnen, und mir die Buffer überschreibt. Das ist eben das, was ich mit Transaktion meine: das Öffnen ist eine solche Transaktion, die nicht unterbrochen werden darf (genau: nicht von einer anderen Dateioperation - sonst schon, sonst wäre die Realtime im Eimer). Bei Abschluss liefert sie einen Pointer auf den ersten Sektor der Datei oder eine Fehlermeldung, erst dann steht das Filesystem für andere Transaktionen wieder zur Verfügung - gern auch im Directory. Gruss Reinhard
>Die Frage ist >allerdings: darf ich davon ausgehen, dass käufliche SD/MMC-Karten 512 >Bytes Sektorgrösse haben? Wie sieht es mit SDHC aus? Nimm keine 2GB Karten. Nimm keine 4GB Karten wo nicht SDHC drauf steht. Ich hatte auch mit denen bisher keine Probleme, andere aber wohl schon wenn man mal so rumliest. SDHC hat laut Spezifikation 512 Byte Sektoren.
Hallo, danke nochmals für eure Bemühungen. Ich habe es jetzt wie folgt implementiert: Alle Funktionen, wo der FAT oder Directory Buffer modifiziert wird, sind mit einem Semaphor geschützt. Das scheint ziemlich gut zu funktionieren :-) Mein Filesystem kann jetzt jedenfalls schon verschiedenste Karten erkennen, FAT16 und FAT32 unterscheiden, anhand eines angegebenen Pfades eine Datei öffnen, sowie Attribute lesen und verändern. Ich habe mir in meinem Code mit printf() ein paar Debugmeldungen eingebaut, und mein ARM Board mit dem Terminal verbunden. Macht schon Spass, wenns da rattert, und man sieht, wie da Zeug auf der Karte gesucht (und gefunden :-) ) wird. Es scheint bisher also zu funktionieren :-) auch mit Langen Dateinamen. Ich muss nur noch Funktionen zum Schreiben implementieren und das Lesen noch ein wenig Effizienter machen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.