Hallo,
ich stehe momentan vor der Aufgabe meinen Source code für zukünftige
Projekte modularer zu gestalten. Als Beispiel soll hier ein einfacher
Mittelwertfilter dienen.
Ich könnte jetzt den Mittelwertfilter als Modul definieren und den
Speicher extra als Variable deklarieren. Das Modul wäre für alle
Speichergrößen gleich. Die Länge wäre von der Speichergröße und der
Initialisierung abhängig. Ich benötige aber keine flexible Länge die
sich zur Laufzeit ändert. Ich muss jezt aber für jeden Filter den ich
benutze den Speicher mit definierter Länge anlegen. Wahrscheinlich kann
man dass über Macros usw. machen. Es bleiben aber wohl immer min. 2
Zeilen eine für den Speicher eine für die Initialisierung.
Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für
den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden
nie wieder freigegeben. Somit würde auch keine fragmentierung auftreten.
Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer
gleich wäre. Natürlich wäre die Systemstartzeit etwas erhöht.
Gibt es andere Auswirkungen, die ich jetzt nicht auf dem Schirm habe?
Oder ist das aus anderen Gründen ein NoGo bei der (sehr) hardwarenahen
Softwareentwicklung?
Alfons H. schrieb:> Es bleiben aber wohl immer min. 2 Zeilen eine für den Speicher eine für> die Initialisierung.
Wenn du dem implizit genannten C noch ein ++ gönnst, geht's in einer.
Oliver
Alfons H. schrieb:> Sorry. Hatte ich vergessen. Es geht um C. Nicht C++
Darf ich fragen, warum man sich freiwillig eine solche Kugel ans Bein
bindet?
(und nein, ich brauche/erwarte keine Antwort auf die Frage)
Alfons H. schrieb:> Ich könnte jetzt den Mittelwertfilter als Modul definieren und den> Speicher extra als Variable deklarieren.
Ich lasse der Applikation beim Init einen Speicher notwendiger Größe
übergeben. Pseudo Beispiel (ohne Fehlermeldung, Success etc).
Die Applikation stellt das RAM bereit, wie sie will. Stack, malloc,
static, ... . Und Modul richtet alle instanzabhängigen Daten darin ein.
Wenn es nur eine Instanz gibt, fällt struct sModul für Header und
Applikation auch noch weg. Dann ist nur der Speicher für N-Elemente zu
übergeben. Teils auch anders rum: Du übergibst x Byte Speicher und
bekommst die Anzahl an Elementen, die reinpassen.
Alfons H. schrieb:> Diese Speicher würden nie wieder freigegeben.
Bei Echtzeit ist natürlich schnelle Reaktion erwünscht. So gesehen, kann
das vorteilhaft sein (bis das nächste SW-Update etwas MEHR Speicher
möchte).
Alfons H. schrieb:> Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer> gleich wäre.
Die Laufzeit wäre bei den Durchläufen gleich, aber nicht zwangsläufig
identisch zu der bei einer schon zur Compilezeit bekannten
Speicheradresse.
@Bruno
ja. Das ist klar. Aber hier muss man eben die Struktur und den Speicher
getrennt instanziieren. Mit malloc in der Init funktion würde das
automatisch geschehen. Nur liegt der Speicher halt nicht bei den
globalen Variablen sondern im heap
Lu schrieb:> Alfons H. schrieb:>> Diese Speicher würden nie wieder freigegeben.>> Bei Echtzeit ist natürlich schnelle Reaktion erwünscht. So gesehen, kann> das vorteilhaft sein (bis das nächste SW-Update etwas MEHR Speicher> möchte).
Ich gehe davon aus, dass bei einem softwareupdate der Aufbau des RAMs
der Applikation sich sowieso ändert.
Walter T. schrieb:> Alfons H. schrieb:>> Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer>> gleich wäre.>> Die Laufzeit wäre bei den Durchläufen gleich, aber nicht zwangsläufig> identisch zu der bei einer schon zur Compilezeit bekannten> Speicheradresse.
Ist die Frage ob der Compiler versteht, dass wenn ich 3 Strukturen mit
pointer auf festen Speicher erstelle und diese dem Modul übergebe dass
er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch
nicht fest stehen.
Lu schrieb:> Bei Echtzeit ist natürlich schnelle Reaktion erwünscht.
Ist das so?
Besser:
> Der Begriff Echtzeit charakterisiert den Betrieb von Echtzeitsystemen, die
bestimmte Ergebnisse zuverlässig innerhalb einer vorbestimmten Zeitspanne, zum
Beispiel in einem festen Zeitraster, liefern können
Das kann möglichst schnell bedeuten, muss aber nicht. Zu schnell kann
sogar kontraproduktiv sein.
Alfons H. schrieb:> [ob]> er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch> nicht fest stehen.
Bei Inline-Funktionen: Ja, allerdings habe ich eher ältere GCC-Versionen
getestet.
Aber meistens wird das nicht viel ausmachen, im Idealfall nur das laden
der Offset-Adresse.
Wenn ich das richtig verstehe dann geht es um den ewigen Streit ob man
dynamische Speicher (Heap) benutzen darf oder ob das böse ist.
Die Antwort lautet: kommt drauf an. Das Internet ist voll von Artikeln
darüber. Chat GPT wird dir die Vor- und Nachteile ausführlichst erklären
wenn du deine Randbedingung nennst.
Die kurze Antwort: Ja klar ist es in Ordnung, dynamischen Speicher bei
der Initialisierung, also zur Laufzeit zu reservieren. Du darfst halt
niemals in die Lage kommen dass dir der Speicher zur Laufzeit ausgeht.
Dafür das von vornherein auszuschließen, gibt’s Techniken.
Alfons H. schrieb:> Mit malloc in der Init funktion würde das> automatisch geschehen. Nur liegt der Speicher halt nicht bei den> globalen Variablen sondern im heap
+ Du brauchts malloc (manchmal unerwünscht, weil sich nicht jeder an
"nur zu Beginn" hält)
+ keine Freigabe/Wiederverwendung möglich
+ nicht alternativ auf Stack möglich
+ keine statische Prüfung des Speicherplatzes
Wenn der Aufrufer den Speicherplatz bereitstellt, werden viele Dinge im
Modul viel einfacher. Gehst Du dann über zu beliebig vielen Instanzen
(auch temporär) wird es noch einfacher, weil alle lokalen Variablen
wegfallen. Man ist dann zu einem sauberen Design gezwungen. Und der
Linker meckert sofort, wenn man sich beim Speicher total verschätzt hat.
Um die Laufzeit wegen der Indirektion brauchst Du Dir keine Gedanken
machen.
Du brauchst kein malloc, kannst doch den Speicher im caller auch
statisch anlegen. Dann kann der Compiler auch besser prüfen, ob es
overflows gibt. Wenn sich die Anzahl der Mittelwertfilter Instanzen oder
deren Pufferlänge nicht ändern zur Laufzeit ist doch alles tutti. In C
geht's mit Makros, in C++ mit constexpr
Alfons H. schrieb:> Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für> den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden> nie wieder freigegeben.
Ja, das kannst du so machen.
Die C++ -Lösung würde auch dieses Problem ersparen:
Alfons H. schrieb:> Natürlich wäre die Systemstartzeit etwas erhöht.
Und der Linker würde dir direkt sagen können wie viel Speicher benutzt
wird und wie viel noch frei ist, statt dass du dafür erst flashen musst.
Alfons H. schrieb:> Es geht um C. Nicht C++
Aber dann geht's natürlich nicht.
Alfons H. schrieb:> Jan K. schrieb:>> In C>> geht's mit Makros, in C++ mit constexpr>> Willst du mir ein Beispiel zeigen wie das geht?
Ziemlich genau so, wie hier von Bruno schon gezeigt wurde:
Beitrag "Re: Speicherverwaltung bei modularer Echtzeitsoftware"
Die Idee ist also, dass der Aufrufer den Speicher reserviert und auch
die Instanz struct erstellt, nicht dein Filter Modul. Die Instanz struct
wird per Zeiger übergeben und ist somit dein "Objekt" des Filtermoduls.
Damit wird der state des Filters zu 100% beschrieben und du brauchst
keine static variable mehr im Filtermodul und du kannst, so es denn
gewünscht ist, beliebig viele Filter "Objekte" parallel erstellen.
Für C++ hat es Niklas über mir schon gezeigt. Braucht sogar kein
constexpr, nur templates.
Jan K. schrieb:> dass der Aufrufer den Speicher reserviert und auch> die Instanz struct erstellt, nicht dein Filter Modul.
den Speicher ja, aber die Instanz des structs erstellt die Init-Funktion
des Moduls "in" dem reserviertem Speicher.
Das struct selber ist der Applikation gar nicht bekannt. Wenn an nur
einen Ptr davon verwendet (woanders "Händler/hdl" genannt), ist das
typsicher und OK.
Bruno V. schrieb:> ist das typsicher und OK.
Naja, du musst sicher sein dass in MODUL_RAM_SIZE die Größe des struct
korrekt eingetragen ist, und die kann schnell mal geändert werden, und
kann auch je nach Plattform oder Compiler -Optionen anders sein.
Besonders "sicher" finde ich das nicht.
Außerdem fehlt da ein _Alignas bei "modul_ram"; das muss ebenso händisch
konsistent gehalten werden.
Niklas G. schrieb:> Naja, du musst sicher sein dass in MODUL_RAM_SIZE die Größe des struct> korrekt eingetragen ist, und die kann schnell mal geändert werden, und> kann auch je nach Plattform oder Compiler -Optionen anders sein.
Sie muss nur >= sein und per CT_ASSERT abgesichert. Die Alternative ist,
die Strukturen zu veröffentlichen. Das ist zwar in C++(-Klassen) üblich,
aber möglichst zu vermeiden.
Bruno V. schrieb:> Sie muss nur >= sein und per CT_ASSERT abgesichert
Wenn sie zu groß ist, verschwendet man Speicher. Man sollte also genau
treffen. Dann braucht es aber Fallunterscheidungen je nach Architektur,
Compiler -Optionen etc.
Bruno V. schrieb:> Das ist zwar in C++(-Klassen) üblich, aber möglichst zu vermeiden.
Naja, ein Embedded-Projekt das so groß ist dass das zum Problem wird,
kann wahrscheinlich auch einfach "new" benutzen, weil dann eh massig
(SD)RAM vorhanden ist.
Alfons H. schrieb:> Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für> den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden> nie wieder freigegeben. Somit würde auch keine fragmentierung auftreten.> Auch die Laufzeit wäre immer gleich weil der Ablauf der allocation immer> gleich wäre. Natürlich wäre die Systemstartzeit etwas erhöht.
malloc beim Systemstart ist nicht unübliches und ein gangbarer Weg.
Einen vordefinierten Speicherbereich zu übergeben und darauf zu hoffen,
das der dem Aufgerufenen langt erleichter die Pflege und Integration
eher nicht. Dann lieber wirklich die gesamte Resourcenstruktur "public"
machen. Bei C++ ist das quasi der Normalfall. (Ja wir werden bei uns
auch zu C genötigt, nervt aber was solls...)
In manchen Fällen hat malloc/free auch zur Laufzeit Sinn, z.B. wenn es
sich um Zusatzfunktionen handelt, wo man mit leben kann wenn die mal
temporär nicht tun. (Z.B. Integrierte Webserver, Dateisysteme etc). Bei
uns ist es so, dass beim Systemstart alle Module ihren benötigten RAM so
weit es geht per malloc holen, manche Module machen es erst in der
Konfigurationsphase. Zur Laufzeit ist Malloc dann in allen Echtzeit und
Hauptfunktionen jedoch quasi "verboten". Das hat auch den Vorteil das
man sich die ganze Fehlerbehandlung sparen kann, da es per Definition
zur Laufzeit kein "Out Of Memory" geben kann.
Niklas G. schrieb:> Naja, ein Embedded-Projekt das so groß ist dass das zum Problem wird,
Dabei geht es nur darum, Internas von Modulen nicht unnötig zu
veröffentlichen. Egal ob für einen µC mit 1k oder 1G.
Niklas G. schrieb:> In C++ gibt's dafür "private".
private schränkt den Zugriff ein. Nicht die Veröffentlichung. (Das soll
aber kein C/C++ Dingen werden: In beiden kann man natürlich auch nur
Teile veröffentlichen und den Rest lokal erweitern)
Bruno V. schrieb:> Nicht die Veröffentlichung.
Warum möchtest du die Interna eines Moduls für andere Komponenten der
selben Anwendung (!) geheim halten?
Der Preis für statische Speicherverwaltung ist dass man die Größen der
Klassen kennt. Der Compiler kann da wunderbar automatisch für sorgen.
Kein schlechter Preis finde ich. Nervig wird das nur bei sehr großen
Projekten, wo eine Änderung einer Klasse eine Neukompilation vieler
anderer Dateien nach sich zieht, aber wie gesagt sind das nicht die
Projekte, wo statische Speicherverwaltung wichtig ist.
Alfons H. schrieb:> Ist die Frage ob der Compiler versteht, dass wenn ich 3 Strukturen mit> pointer auf festen Speicher erstelle und diese dem Modul übergebe dass> er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch> nicht fest stehen.
Soll es möglichst schnell sein oder schnell genug?
G. K. schrieb:> Alfons H. schrieb:>>> Ist die Frage ob der Compiler versteht, dass wenn ich 3 Strukturen mit>> pointer auf festen Speicher erstelle und diese dem Modul übergebe dass>> er das besser optimieren kann als wenn die Adressen zu dieser Zeit noch>> nicht fest stehen.>> Soll es möglichst schnell sein oder schnell genug?
Natürlich nur schnell genug. Also ein paar Takte für indirekte
Adressierung usw. sind kein Problem. Mir ist klar dass es immer ein
Kompromiss wird. Zu viel Abstraktion lohnt sich auch nicht, da man dann
ja praktisch bei C++ landet und man hier das Rad nicht neu erfinden
müsste.
Mir ging es nur darum ob ich etwas übersehe bei der Verwendung von
Speicherallokation zur Laufzeit.
Bleibt die Frage wie das meine Kollegen sehen, da malloc bei
hardwarenahen Entwicklern ja doch den Ruf hat etwas "Böses", was man auf
keinen Fall verwendet, zu sein scheint.
Moin,
Mal abgesehen von irgendwelchen exotischen Prozessoren, die mit
unterschiedlich zu adressierenden RAM-Bereichen arbeiten koennen, wirds
doch ausser fuer die Position im RAM voellig wurst sein, ob ich meinen
Pointer auf ein Stueck RAM nun per
1
ptr=calloc(RUELPS,sizeof(sample_t));
oder per
1
staticsample_tptr[RUELPS];
kriege.
Ich wuerde mal vermuten, dass es da bei den Modularisierungbemuehungen
noch deutlich lohnendere Ziele geben wird.
Gruss
WK
Dergute W. schrieb:> wirds> doch ausser fuer die Position im RAM voellig wurst sein,
- Der Aufruf an calloc/malloc/new kostet (etwas) Laufzeit und natürlich
Flash
- Man sieht beim Kompilieren/Linken nicht den gesamten RAM-Verbrauch
sondern kann ihn nur durch Ausführen ermitteln
- Man muss alle malloc-Aufrufe mit Fehlerbehandlung versehen
- Ist der Verbrauch zu hoch, bemerkt man dies erst beim Ausführen und
muss dann ggf. mühselig nachvollziehen, welcher der (ggf. vielen)
malloc-Aufrufe zu viel Speicher anfordert (nicht notwendigerweise der
Aufruf, der dann 0 zurückgibt weil kein Speicher mehr frei ist)
- Der Linker kann unbenutzte dynamisch angeforderte Speicherbereiche
nicht wegoptimieren, auch wenn nie darauf zugegriffen wird; statisch
allokierte Variablen aber schon, was das
Umstrukturieren/Variantenbildung vereinfacht (gerade auch beim
Entwickeln/Testen)
- Man kann leicht versehentlich unbemerkt weitere malloc-Aufrufe nach
der Initialisierungsphase einfügen sodass dann ggf. doch
Fragmentierungsprobleme oder Verzögerungen auftreten
- Man kann per dynamisch angeforderten Speicher auch nur dynamisch
initialisieren, während statische Variablen mit fixer Initialisierung
versehen werden kann, die dann vom Startup-Code sehr effizient
automatisch vom Flash in den RAM kopiert wird
Ich finde
1
Filter<8>filter1;
2
Filter<4>filter2;
wunderbar simpel vom Handling her, es funktioniert einfach und
effizient, und man kriegt "gratis" vom Linker die Aufsummierung/Prüfung
des Speicherverbrauchs, mit Zusammenfassung im Map-File und schicker
grafischer Darstellung in manchen IDEs. Man kann so natürlich auch
C-Datenstrukturen "verpacken" und die Speicher-Reservierung in eine
"Top-Level" C++-Datei verpacken und den Rest des Codes als C belassen.
Niklas G. schrieb:> Warum möchtest du die Interna eines Moduls für andere Komponenten der> selben Anwendung (!) geheim halten?
Es gibt viele Herangehensweisen an SW. Manche schwören auf Doxygen und
erzeugen damit wunderbare PDFs zum Preis riesiger
Interface-Header-Dateien.
Wir halten Header kurz mit minimalen Kommentaren. Da würden in modul.h
wirklich nur 2-3 Forward-Structs drinstehen (als Hdl) und ein Haufen
Funktionen für diese Hdl. Insgesamt vielleicht zwei Dutzend Zeilen.
Keine Implementierungs-Details wie den Aufbau der Strukturen. Sie sind
trotzdem nicht geheim, da jeder Zugriff hat, auch auf die Libs.
> Der Preis für statische Speicherverwaltung ist dass man die Größen der> Klassen kennt.
Die Speicherverwaltung ist in meinem Beispiel ja auch statisch. Zum
Preis eines obskuren und per CT-Assert überwachten Defines.
Bruno V. schrieb:> Keine Implementierungs-Details wie den Aufbau der Strukturen.
Wenn es dir um die Übersicht geht, kannst du ja auch zwei Header machen;
einen mit der Forward-Deklaration, einen mit der kompletten Definition
des structs. Nur die Source-Datei, welche die Instanz tatsächlich anlegt
sowie die Datei die die Datenstruktur implementiert muss dann den Header
mit der ganzen Definition inkludieren.
Bruno V. schrieb:> Zum> Preis eines obskuren und per CT-Assert überwachten Defines.
widerspricht ein obskures Makro nicht ein bisschen der Idee die Datei so
übersichtlich wie möglich zu halten?
Alfons H. schrieb:> Nun wäre die Frage ob man bei der Initialisierung nicht den Speicher für> den Filter mittels malloc im Heap anlegen könnte. Diese Speicher würden> nie wieder freigegeben.
Kann man so machen. Aber bei
> (sehr) hardwarenahen Softwareentwicklung
gibt es auch Ansätze, die weniger Resourcen benötigen wie zum Beispiel
alloca: Den Speichen in main per alloca allokieren und die
Speicheradresse ans Modul übergeben per Funktionsargument oder globaler
Variable.
alloca benötigt weniger Resourcen als malloc weil:
1) alloca legt den Speicher auf dem Stack an, Allokation besteht also
nur aus einer Manipulation des Stackzeigers statt einer komplexen
Routine wie bei malloc. alloca spart also Programmspeicher.
2) alloca allokiert weniger Speicher als malloc, weil malloc noch etwas
Platz für Verwaltungsinformationen braucht wie z.B. Größe des
allokierten Bereichs, Listenverwaltung, etc. alloca spart also etwas
RAM.
3) alloca spart auch Laufzeit, weil es wesentlich einfacher ist als
malloc.
Die Einschränkung ist natürlich, dass die Lebensdauer eines mit alloca
allokierten Objekts dem einer lokalen Variable entspricht. Weil main
nie verlassen wird und das Objekt nie freigegeben wird ist das aber kein
Problem.
Alfons H. schrieb:> da malloc bei hardwarenahen Entwicklern ja doch den Ruf hat etwas> "Böses", was man auf keinen Fall verwendet, zu sein scheint.
Genauso wie goto.
In kleinen embedded Systemen gibt's halt das Problem, dass es für ein
fehlgeschlagenes malloc keine sinnvolle Fehlerbehandlung gibt.
Soll ein ABS-System dann nur auf drei Rädern bremsen, oder so?
Entweder man weiss vorher, wieviel Speicher man benötigt, oder das
Design ist fehlerhaft by Design.
Oliver
Johann L. schrieb:> Die Einschränkung ist natürlich, dass die Lebensdauer eines mit alloca> allokierten Objekts dem einer lokalen Variable entspricht.
Was soll alloca dann überhaupt bringen gegenüber einer lokalen
Variablen?
Alloca hätte nur dann einen Vorteil, wenn die Länge zur Compilezeit noch
nicht feststeht und z.B. erst über ein Kommando übergeben wird.
Dafür muß man aber die Startadresse und die Länge immer zusätzlich
mitführen.
Eine Variable ist dagegen dem Compiler bekannt, ihr Name ist die
Startdaresse und sizeof() ihre Länge.
Peter D. schrieb:> Was soll alloca dann überhaupt bringen gegenüber einer lokalen> Variablen?
alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur
Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem
Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen
verwenden)
Bruno V. schrieb:> alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur> Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem> Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen> verwenden)
Das kann man auch genauso gut mit einem statisch allokierten Array
machen, das man dann dem jeweils aktiven Modul übergibt. Oder einfach
per union.
Peter D. schrieb:> Johann L. schrieb:>> Die Einschränkung ist natürlich, dass die Lebensdauer eines mit alloca>> allokierten Objekts dem einer lokalen Variable entspricht.>> Was soll alloca dann überhaupt bringen gegenüber einer lokalen> Variablen?
Semantisch gleich aber die Syntax ist etwas anders. Mit alloca:
1
#include<alloca.h>
2
3
intmain(void)
4
{
5
void*memory=alloca(get_alloc_size());
6
...
7
}
Ohne alloca:
1
intmain(void)
2
{
3
charmemory[get_alloc_size()];
4
...
5
}
> Alloca hätte nur dann einen Vorteil, wenn die Länge zur Compilezeit noch> nicht feststeht und z.B. erst über ein Kommando übergeben wird.
Das geht auch mit einer Variablen, siehe obiger Code. Allerdings kann
es sein, dass neuere C-Standards rumzicken, weil die Größe nicht bekannt
ist.
> Dafür muss man aber die Startadresse und die Länge immer zusätzlich> mitführen.
Die Länge ist dem Modul ja bekannt. Oder man kann sie übergeben als
get_alloc_size() mit alloca oder auch als sizeof(memory) im 2ten
Beispiel.
> Eine Variable ist dagegen dem Compiler bekannt, ihr Name ist die> Startdaresse und sizeof() ihre Länge.
Bringt aber nix. Zumindest dann nicht, wenn der Speicher wie oben in
main allokiert wird, weil im Modul kann man ja kein sizeof verwenden.
sizeof funktioniert ja nur in main.
Einfachste Lösung ist get_alloc_size() zu übergeben oder im Modul
aufzurufen. Ist immer noch weitaus billiger als malloc.
Eine denkbare Bremse wäre hingegen, wenn z.B. malloc langsames externes
RAM allokiert und man nicht genug (schnelles) internes RAM dafür hat.
Bei vielen kleinen embedded Systemen wird aber nur eine RAM-Art
verwendet.
Niklas G. schrieb:> Bruno V. schrieb:>> alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur>> Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem>> Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen>> verwenden)>> Das kann man auch genauso gut mit einem statisch allokierten Array> machen, das man dann dem jeweils aktiven Modul übergibt.
Bei einem statischen Array muss die Größe zur Compilezeit vorliegen.
Bei alloca ist das nicht der Fall.
Johann L. schrieb:> Bei einem statischen Array muss die Größe zur Compilezeit vorliegen.
Aber das hat dann nichts mit der dynamischen Umschaltung zwischen zwei
Modulen zu tun. Es sind zwei orthogonale Probleme:
- Größe zur Laufzeit bekannt
- Zwischen 2 Modulen umschalten
Erstes kann man mit malloc, alloca, variable-length-arrays lösen.
Zweites mit union oder Anlegen eines ausreichend großen Arrays. alloca
ist also nicht, wie von die gesagt, die Lösung des 2. Problems.
Nur wenn beide Probleme gleichzeitig auftreten kann man alloca in
Kombination mit der dynamischen Umschaltung nutzen, oder auch eine der
Alternativen; die Vorteile von alloca haben nichts mit der dynamischen
Umschaltung zu tun.
Niklas G. schrieb:> Johann L. schrieb:>> Bei einem statischen Array muss die Größe zur Compilezeit vorliegen.>> Aber das hat dann nichts mit der dynamischen Umschaltung zwischen zwei> Modulen zu tun. Es sind zwei orthogonale Probleme:> - Größe zur Laufzeit bekannt> - Zwischen 2 Modulen umschalten>> Erstes kann man mit malloc, alloca, variable-length-arrays lösen.> Zweites mit union oder Anlegen eines ausreichend großen Arrays. alloca> ist also nicht, wie von die gesagt, die Lösung des 2. Problems.
Ist dann einfach:
wenn die Module des Speicher exklusiv nutzen.
Beispiel wäre eine Game-App, die mehrere Spiele ausführen kann. Da läuft
immer nur 1 Spiel, und daher kommen sich mehrere Spiele -- die ja immer
zeitlich nachenander ausgeführt werden -- nicht in die Quere.
Ginge auch mit einer Union. Was weniger schön ist, weil alle lokalen
Datenstrukturen global sichtbar sein müssen, damit main sie sieht.
Je nach verwendeter Toolchain gibt es auch Insellösungen, die keine
globale Union und kein explizites Allokieren beötogen. Zum Beispiel
könnte man mit avr-gcc in allen Teilmodulen, die den exklusiven Speicher
verwenden wollen:
1
externmemory1_tmemory1__asm("__heap_start");// in Modul 1
2
externmemory2_tmemory2__asm("__heap_start");// in Modul 2
Johann L. schrieb:> Ist dann einfach:void *memory = alloca (MAX (get_mem1_size(),> get_mem2_size()));> wenn die Module des Speicher exklusiv nutzen.
Und warum nicht:
Welchen Vorteil hat alloca hier, denn es nicht hätte, wenn es nur
ein Modul gäbe?
Johann L. schrieb:> Zum Beispiel> könnte man mit avr-gcc in allen Teilmodulen, die den exklusiven Speicher> verwenden wollen:
Wenn man schon die GNU-Tools voraussetzt, kann man auch ein OVERLAY im
Linkerscript definieren für die einzelnen Module, dann prüft immerhin
der Linker ob der Speicher reicht.
Niklas G. schrieb:> Johann L. schrieb:>> Ist dann einfach:void *memory = alloca (MAX (get_mem1_size(),>> get_mem2_size()));>> wenn die Module des Speicher exklusiv nutzen.>> Und warum nicht:
>> Welchen Vorteil hat alloca hier, den es nicht hätte, wenn es nur> ein Modul gäbe?
1) Objekte im Static Storage brauchen eine zur Compilezeit bekannte
Größe in C.
2) alloca braucht weniger RAM, weniger Programmspeicher und weniger
Laufzeit, wie bereits hier ausgeführt:
Beitrag "Re: Speicherverwaltung bei modularer Echtzeitsoftware"
Ich hatte auch schon erwähnt, dass neuere C-Versionen VLAs nicht mögen.
> Johann L. schrieb:>> Zum Beispiel>> könnte man mit avr-gcc in allen Teilmodulen, die den exklusiven>> Speicher verwenden wollen:>> Wenn man schon die GNU-Tools voraussetzt, kann man auch ein OVERLAY im> Linkerscript definieren für die einzelnen Module, dann prüft immerhin> der Linker ob der Speicher reicht.
Viele Anwender bevorzugen Lösungen, die kein eigenes Linker-Skript
brauchen. Aber wer's mag...
Wobei sich der wirkliche RAM-Verbrauch erst zur Laufzeit ergibt. Zur
Abschätzung hilf dann z.B.
https://avrdudes.github.io/avr-libc/avr-libc-user-manual-2.3.0/group__util__ram__usage.html
Johann L. schrieb:> 1) Objekte im Static Storage brauchen eine zur Compilezeit bekannte> Größe in C.
Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen
umschalten möchte.
> 2) alloca braucht weniger RAM, weniger Programmspeicher und weniger> Laufzeit, wie bereits hier ausgeführt:> Beitrag "Re: Speicherverwaltung bei modularer Echtzeitsoftware"
Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen
umschalten möchte.
> Ich hatte auch schon erwähnt, dass neuere C-Versionen VLAs nicht mögen.
Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen
umschalten möchte.
Johann L. schrieb:> Wobei sich der wirkliche RAM-Verbrauch erst zur Laufzeit ergibt.
Bei Verwendung von OVERLAY, der C++ -Lösung, globalen/statischen
Variablen nicht.
Niklas G. schrieb:> Das hat nichts damit zu tun, wenn man zwischen mehreren Modulen> umschalten möchte.
Warum fragst du dann danach?
Bei Verwendung von alloca ist es egal, wie viele Module man verwendet.
> Johann L. schrieb:>> Wobei sich der wirkliche RAM-Verbrauch erst zur Laufzeit ergibt.>> Bei Verwendung von OVERLAY, der C++ -Lösung, globalen/statischen> Variablen nicht.
Und wie will dein OVERLAY den Stack-Verbrauch erkennen?
Johann L. schrieb:> Warum fragst du dann danach?
Weil hier das Gegenteil behauptet wurde, dem ich widersprochen hatte,
dem du wiederum widersprochen hattest:
Bruno V. schrieb:> alloca (oder "Aufrufer stellt bereit") ermöglicht es, einen Speicher zur> Laufzeit einem von n Modulen zu geben. Z.B. abhängig von einem> Dipschalter Modul A oder Modul B (die unterschiedliche Strukturen> verwenden)
Sinnlose Diskussion wenn man meine Beiträge nicht genau liest.
Johann L. schrieb:> Und wie will dein OVERLAY den Stack-Verbrauch erkennen?
Es ist nicht "mein" OVERLAY, und den Stackverbrauch kann man anderweitig
ermitteln, außer man benutzt alloca() oder variable length arrays...
Johann L. schrieb:> Der TO sucht eine Lösung in C.
Richtig. Manche Leser könnten aber an der C++ Lösung interessiert sein.
Tobias schrieb:> Ein ganzer Thread über Echtzeit-Bedingungen und kein einziges Mal wird> ein Echt-Zeit Betriebssystem erwähn
Wie soll ein Echtzeitbetriebssystem die Speicherverwaltung echtzeitfähig
machen, wenn sie das zuvor nicht war?
Niklas G. schrieb:> Sinnlose Diskussion wenn man meine Beiträge nicht genau liest.
Naja, Deine Lösung "array bereitstellen" habe ich genauso als Beispiel
auskodiert und explizit empfohlen. Da haben wir keinen Dissens.
Und dass es zwei unterschiedliche Aufgaben sind (Größer zur Compilezeit
und Aufteilung auf 2 Module), ist Konsens.
Dissens gibt es quasi nur bei der Union:
* weil sie 2 (oder mehr) unabhängige Module verbinden
* gar nicht funktionieren, wenn es nicht 1 von 2 sondern z.B. 3-7 von
12 Modulen sind. Ein typisches Modul wäre die Instanz eines
Uart-Treibers, wo abhängig vom Gegenüber 1..10k Fifo gewünscht werden
und wo es 1..5 verschiedene geben kann.
Und dabei, dass alloca ein bequemer Standardweg ist und ein paar kleine
Vorteile gegenüber meiner manuellen Verteilung hat.
(Dein implizit oben angebrachtes Argument, dass ab x kB oder MB alles
egal ist und man darauf nicht mehr achten muss, ist in meinem Umfeld
nicht stichhaltig. Die Probleme bleiben die gleichen)