Speicherverwaltung bei modularer Echtzeitsoftware

OP #7925096
Lesenswert?

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?

#7925126
Lesenswert?

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.

1
/*modul.h*/
2
#define MODUL_RAM_SIZE(Elemente) (128+(14*Elemente))
3
struct sModul; /* Inhalt bleibt der Applikation verborgen! Nur Ptr! */
4
struct sModul *ModulInit(unsigned char *ram, int ram_size);
5

6
/* application.c */
7
struct sModul *ModulHdl;
8
static unsigned char modul_ram[MODUL_RAM_SIZE(100)];
9

10
   ModulHdl = ModulInit(modul_ram, sizeof(modul_ram));
11

12
   ModulDoThis(ModulHdl);
13
   ModulAddThat(ModulHdl, xy);
14
   z = ModulGetThose(ModulHdl);
15

16

17
/*modul.c*/
18
struct sModul
19
{
20
   int ...
21
};
22

23
struct sElement
24
{
25
   unsigned char ...
26
};
27

28
struct sModulHdl *ModulInit(unisgned char *ram, int ram_size)
29
{
30
struct sModulHdl *m = (struct sModulHdl*) ram;
31
    ....
32
    return m;      
33
}
OP #7925147
Lesenswert?

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.

(Firma: Gast) #7925149
Lesenswert?

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.

#7925160
Lesenswert?

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.

#7925165
Lesenswert?

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.

: Bearbeitet durch User
#7925197
Lesenswert?

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

Persönliche Seite #7925224
Lesenswert?

In C++ wär's einfach:

1
#include <cstddef>
2
#include <cstdint>
3

4
template <std::size_t Depth>
5
struct Filter {
6
  std::int32_t lastValues [Depth];
7
  Filter ();
8

9
  void run ();
10
};
11

12
template <std::size_t Depth>
13
Filter<Depth>::Filter () {
14
  // Irgendwas initialisieren...
15
  for (auto& x : lastValues)
16
    x = 42;
17
}
18

19
template <std::size_t Depth>
20
void Filter<Depth>::run () {
21
}
22

23
Filter<8> filter1;
24
Filter<4> filter2;
25

26
int main () {
27
  filter1.run ();
28
  filter2.run ();
29
}

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.

#7925261
Lesenswert?

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.

#7925279
Lesenswert?

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.

Persönliche Seite #7925282
Lesenswert?

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.

#7925293
Lesenswert?

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.

Persönliche Seite #7925297
Lesenswert?

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.

#7925308
Lesenswert?

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.

Persönliche Seite #7925444
Lesenswert?

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.

OP #7925498
Lesenswert?

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.

#7925510
Lesenswert?

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
static sample_t ptr[RUELPS];

kriege.

Ich wuerde mal vermuten, dass es da bei den Modularisierungbemuehungen noch deutlich lohnendere Ziele geben wird.

Gruss WK

Persönliche Seite #7925516
Lesenswert?

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.

#7925528
Lesenswert?

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.

Persönliche Seite #7925530
Lesenswert?

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?

Persönliche Seite #7925576
Lesenswert?

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.

: Bearbeitet durch User
#7925596
Lesenswert?

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

: Bearbeitet durch User
#7925601
Lesenswert?

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.

Persönliche Seite #7925633
Lesenswert?

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.

Persönliche Seite #7925639
Lesenswert?

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
int main (void)
4
{
5
    void *memory = alloca (get_alloc_size());
6
    ...
7
}

Ohne alloca:

1
int main (void)
2
{
3
    char memory[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.

Persönliche Seite #7925641
Lesenswert?

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.

Persönliche Seite #7925658
Lesenswert?

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.

Persönliche Seite #7925667
Lesenswert?

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:

1
void *memory = alloca (MAX (get_mem1_size(), get_mem2_size()));

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
extern memory1_t memory1 __asm("__heap_start"); // in Modul 1
2
extern memory2_t memory2 __asm("__heap_start"); // in Modul 2
Persönliche Seite #7925678
Lesenswert?

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:

1
char memory [MAX (get_mem1_size(), get_mem2_size())];

oder

1
static char memory [MAX (get_mem1_size(), get_mem2_size())];

oder

1
char* memory = malloc (MAX (get_mem1_size(), get_mem2_size()));

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.

Persönliche Seite #7925684
Lesenswert?

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:

1
> char memory [MAX (get_mem1_size(), get_mem2_size())];

oder

1
> static char memory [MAX (get_mem1_size(), get_mem2_size())];

oder

1
> char* memory = malloc (MAX (get_mem1_size(), get_mem2_size()));

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

Persönliche Seite #7925698
Lesenswert?

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.

  1. 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.

Persönliche Seite #7925723
Lesenswert?

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?

Persönliche Seite #7925724
Lesenswert?

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...

Persönliche Seite #7925747
Lesenswert?

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?

#7926114
Lesenswert?

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)

: Bearbeitet durch User

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren