Ich entwerfe ein eingebettetes C-Datenspeichermodul. Es wird von Dateien / Modulen eingeschlossen, die Zugriff auf diese "gemeinsamen" systemweiten Daten haben wollen. Mehrere Aufgaben aggregieren Dutzende von Eingaben (GPIO-, CAN-, I2C- / SPI- / SSP-Daten usw.) und speichern diese Werte mithilfe der API. Dann können andere Aufgaben über die API sicher auf die Daten zugreifen. Das System ist eine eingebettete App mit einem RTOS, daher werden Mutexe zum Schutz der Daten verwendet. Diese Ideen werden unabhängig von der Implementierung verwendet.
Ich habe in der Vergangenheit so etwas entworfen, und ich versuche, es zu verbessern. Ich bin gerade auf halbem Weg durch eine neue Implementierung und ich stolperte in ein paar Schluckauf und würde wirklich von einer neuen Perspektive profitieren.
Kurze Übersicht über die Anforderungen dieses Moduls:
Die Frage ist, wie würdest du so etwas gestalten? Aufzählungen, Strukturen, Accessoren, Makros usw.? Ich suche hier keinen Code, ich diskutiere nur allgemeine allgemeine Designideen. Wenn es eine Lösung im Internet gibt, die diese Art von Dingen anspricht, ist vielleicht sogar nur ein Link ausreichend.
Ich habe eine meiner nur noch nicht akzeptierten Fragen aktualisiert. Hier ist meine letzte Implementierung. Benutze dies seit über einem Jahr und es funktioniert fantastisch. Sehr einfach, Variablen hinzuzufügen und sehr wenig Overhead für den Vorteil, den es uns gibt.
lib_data.h:
%Vor%lib_data.c:
%Vor%Ich war selbst einige Male in dieser Situation. Jedes Mal, wenn ich damit fertig bin, "mein eigenes zu rollen", und ich leide definitiv nicht am Not Invented Here (NIH) -Syndrom. Manchmal machen der Platzbedarf, die Bearbeitungszeit oder Zuverlässigkeits- / Wiederherstellbarkeitsanforderungen nur den am wenigsten schmerzhaften Pfad.
Also, anstatt den großen amerikanischen Roman zu diesem Thema zu schreiben, werde ich hier nur ein paar Gedanken aussprechen, da Ihre Frage ziemlich weit gefasst ist (aber danke, dass Sie zumindest eine Frage stellen und Hintergrund liefern). p>
Ist C ++ auf dem Tisch? Inline-Funktionen, Vorlagen und einige der Boost-Bibliotheken könnten hier nützlich sein. Aber ich schätze, das ist gerade C.
Wenn Sie C99 verwenden, können Sie zumindest Inline-Funktionen verwenden, die einen Schritt über die Makros hinausgehen, wenn es um die Typsicherheit geht.
Vielleicht möchten Sie darüber nachdenken, mehrere Mutexe zu verwenden, um verschiedene Teile der Daten zu schützen. Auch wenn die Aktualisierungen kurz sind, sollten Sie die Daten in Abschnitte unterteilen (z. B. Konfigurationsdaten, Initdaten, Fehlerprotokolldaten, Trace-Daten usw.) und jedem einen eigenen Mutex geben, um die Trichter- / Drosselpunkte zu reduzieren / p>
Sie könnten auch in Erwägung ziehen, den gesamten Zugriff auf die Daten durch eine Serveraufgabe zu führen. Alle liest & amp; Schreibvorgänge durchlaufen eine API, die mit der Server-Task kommuniziert. Die Serveraufgaben ziehen Lese- & amp; schreibt Anfragen in der Reihenfolge ihrer Warteschlange, behandelt sie schnell, indem sie in einen RAM-Spiegel schreibt, Antworten sendet, wenn nötig (zumindest für Leseanforderungen), und puffert dann Daten im Hintergrund an NVM, falls erforderlich. Klingt schwergewichtig im Vergleich zu einfachen Mutexen, hat aber in bestimmten Anwendungsfällen Vorteile. Ich weiß nicht genug über Ihre Bewerbung, um zu wissen, ob dies eine Möglichkeit ist.
Eine Sache, die ich sagen werde, ist die Idee, mit einem Tag (zB eine Liste von enums wie CONFIG_DATA, ADDRESS_DATA, etc.) zu erhalten / setzen, ist ein großer Schritt vorwärts von der direkten Adressierung von Daten (zB "gib mir die 256 Bytes bei Adresse ox42000). Ich habe gesehen, dass viele Shops große Schmerzen erleiden, wenn das ganze physikalische Adressierungsschema endlich zusammenbricht und sie müssen neu facettieren / neu gestalten. Versuchen Sie, das "was" von dem "entkoppelt" zu halten. wie "- Kunden sollten nicht wissen oder sich darum kümmern müssen, wo die Dinge gespeichert sind, wie groß sie sind, etc. (Sie wissen vielleicht schon alles, tut mir leid, wenn das so ist, ich sehe es ständig ...)
>Eine letzte Sache. Du hast Mutexe erwähnt. Vorsicht vor Inversion der Priorität ... das kann dazu führen, dass diese "schnellen Zugriffe" in manchen Fällen sehr lange dauern. Bei den meisten Kernel-Mutex-Implementierungen können Sie dies berücksichtigen, aber oft ist es nicht standardmäßig aktiviert. Nochmal, Entschuldigung, wenn das alte Nachrichten sind ...
Einige Ansätze, mit denen ich Erfahrungen gemacht habe und die ich für ihre eigenen Bedürfnisse gefunden habe. Ich schreibe nur meine Gedanken zu diesem Thema, hoffe, das gibt Ihnen einige Ideen, mit denen Sie gehen können ...
(EntryInGroupID = (groupid << 16) | serial_entry_id)
. Die meisten Systeme, mit denen ich gearbeitet habe, benötigten irgendeine Art von Konfigurationsmodul, normalerweise über flüchtige und nichtflüchtige Speicher. Es ist sehr einfach (und sogar verlockend), dies zu überdenken, oder eine Overkill-Lösung zu bringen, wo sie nicht benötigt wird. Ich habe festgestellt, dass der Versuch, zu diesen Themen "zukunftssicher" zu sein, in vielen Fällen nur eine Verschwendung von Zeit sein wird. Normalerweise ist der einfachste Weg das Beste (in Echtzeit und eingebetteten Systemen sowieso). Außerdem treten Leistungseinbußen auf, wenn Sie das System skalieren, und manchmal bleibt es bei der einfacheren und schnelleren Lösung.
Angesichts der von Ihnen gelieferten Informationen und der obigen Analyse empfehle ich Ihnen daher, mit dem Schlüssel-Wert- oder Direkten-Struktur-Ansatz zu gehen. Ich bin ein persönlicher Fan des "single task" -Ansatzes für meine Systeme, aber es gibt wirklich keine absolute Antwort dafür, und es bedarf einer tieferen Analyse. Für diese Lösungen, die ich nach einer "generischen" und gebrauchsfertigen Lösung gesucht habe, finde ich mich immer selbst in 1-2 Wochen implementiert und erspare mir viele Kopfschmerzen.
Ich hoffe, die Antwort war nicht übertrieben: -)
Normalerweise verwende ich eine einfache wörterbuchähnliche API mit einem int als Schlüssel und einem festen Wert. Dies wird schnell ausgeführt, verwendet eine sehr kleine Menge an Programm-RAM und hat eine vorhersehbare Daten-RAM-Nutzung. Mit anderen Worten, die API der untersten Ebene sieht folgendermaßen aus:
%Vor%Tasten werden zu einer Liste von Konstanten:
%Vor%Sie umgehen verschiedene Datentypen durch Casting. Sucks, aber Sie können Makros schreiben, um den Code ein wenig sauberer zu machen:
%Vor%Das Erzielen der Typensicherheit ist schwierig ohne das Schreiben einer separaten Makro- oder Zugriffsfunktion für jedes Element. Bei einem Projekt benötigte ich eine Validierung der Eingabedaten, und dies wurde zum Typ-Sicherheitsmechanismus.
Wie Sie die physische Speicherung von Daten strukturieren, hängt davon ab, wie viel Datenspeicher, Programmspeicher, Zyklen und separate Schlüssel Sie haben. Wenn Sie viel Programmplatz haben, hacken Sie den Schlüssel, um einen kleineren Schlüssel zu erhalten, den Sie direkt in einem Array nachschlagen können. Normalerweise mache ich den zugrunde liegenden Speicher wie folgt:
%Vor%und iteriere durch. Für mich war das sogar auf sehr kleinen (8-Bit) Mikrocontrollern schnell genug, obwohl es für Sie möglicherweise nicht passt, wenn Sie viele Elemente haben.
Denken Sie daran, dass Ihr Compiler die Schreibvorgänge wahrscheinlich inline oder optimiert, sodass Zyklen pro Zugriff möglicherweise niedriger sind als erwartet.