Hi,
wie ist es möglich in C Variablen für eine bestimmte Zeit im RAM zu
nutzen?
Ich habe eine Funktion, welche wenn sie aufgerufen wird 34 uint_16 Werte
benötigt.
Die Daten werden allerdings über mehrere Funktionen gesammelt und
ausgewertet, das allerdings nicht ständig! Es würden 68 Byte ständig
belegen was Verschwendung wäre.
Gibt es sowas wie:
- erstelle 34 uint_16 Variablen
ini();
calc();
out();
- lösche die 34 uint_16 Variablen wieder
Danke für jede Hilfe!
Gruß AVRli...
Dirk schrieb:> Hallo,>> ja das gibts: Den Stack... deklariere die 34 Variablen innerhalb> einer Funktion, dann leben sie bis zu deren Ende:> void maches ()> {> int bla1 = 123;> int blubb = 321;> // und so weiter [1]> ini();> calc();> out();> }
Damit die Funktionen auch darauf zugreifen können:
1
voidini(uint16_t*werte);
2
voidcalc(uint16_t*werte);
3
voidout(uint16_t*werte);
4
5
voidmaches(void)
6
{
7
uint16_twerte[34];
8
ini(werte);
9
calc(werte);
10
out(werte);
11
}
Beim Verlassen von "maches" wird der Platz automatisch wieder
freigegeben.
Danke, innerhalb einer Funktion ist bekannt.
Mein Problem ist dabei, dass die Aktionen nicht alle in eine einzelne
Funktion hintereinander ablaufen.
Die Daten werden via UART eingesammelt, das Main muss in der Zeit weiter
laufen.
Zur Zeit habe ich die 64 Werte global im Main definiert, das
funktioniert schon mit dem Nachteil der Verschwendung, wenn ich den Teil
nicht benötigt wird.
Gruß AVRli...
AVRli .. schrieb:> Zur Zeit habe ich die 64 Werte global im Main definiert, das> funktioniert schon mit dem Nachteil der Verschwendung, wenn ich den Teil> nicht benötigt wird.
Dann willst du wohl mit dem Heap arbeiten.
Siehe:
https://de.wikipedia.org/wiki/Dynamischer_Speicher
das ist aber einfach so.
Eine Lösung für sowas ist dynamische Speicherallokierung. Dafür brauchst
du aber ein Betriebssystem, oder du musst dir selber einen
Speichermanager schreiben.
Beides lohnt für Mikrocontroller in den wenigsten Fällen.
Stell dir mal die Frage: Wann könnte der Speicher denn immer frei
gegeben werden? Wer wird den Speicher dann weiterverwenden? Kannst du
das mit Worten beschrieben?
Wenn ja, ist ziemlich wahrscheinlich, dass die Lösung von Dirk & Rolf
für dich umsetzbar ist. Wenn nein, dann brauchst du den Speicher
ziemlich sicher dauerhaft.
A. S. schrieb:> Wenn nein, dann brauchst du den Speicher> ziemlich sicher dauerhaft.
Und wenn du ihn nicht dauerhaft brauchst, dann solltest du darüber
nachdenken, was passiert, wenn du ihn kurzzeitig brauchst und er gerade
nicht verfügbar ist. Auf Mikrocontrollern kann das durch Fragmentierung
sehr schnell passieren.
A. S. schrieb:> Eine Lösung für sowas ist dynamische Speicherallokierung. Dafür brauchst> du aber ein Betriebssystem,
Wie kommst du denn auf diesen Trichter?
malloc() gibt's auch für AVRs nun schon seit geraumer Zeit – ganz ohne
Betriebssystem.
S. R. schrieb:> Und wenn du ihn nicht dauerhaft brauchst, dann solltest du darüber> nachdenken, was passiert, wenn du ihn kurzzeitig brauchst und er gerade> nicht verfügbar ist.
Ja.
Das braucht man übrigens in solchen Fällen auch dann, wenn man alles
statisch vorbelegt – aber dann mehr Anforderung reinkommt, als vorbelegt
worden war. Anders gesagt, dynamische Speicheranforderungen, gleich wie
sie bedient werden, müssen immer eine Strategie haben, wie sie mit
Ressourcenknappheit umgehen.
> Auf Mikrocontrollern kann das durch Fragmentierung> sehr schnell passieren.
Keineswegs – nicht in dieser Pauschalität (auch, wenn das immer wieder
gern rezitiert wird).
Fragmentierung gibt's erstens sowieso nur, wenn man unterschiedlich
große Blöcke braucht und dann verschieden wieder freigibt. Klang mir
beim TE nicht unbedingt so, aber vielleicht hat er's auch nur nicht ganz
verständlich formuliert.
Zweitens ist Fragmentierung vor allem dann ein Problem, wenn die
Belegung und Freigabe nicht wirklich sehr zufällig ist. Sofern das
tatsächlich guter Zufall ist und stets im Mittel „ein wenig mehr“ an
Speicher da ist als insgesamt nötig ist, dann klappt das mit dem
Sich-selbst-Defragmentieren ganz gut. Selbstverständlich wirst du immer
einen pathologischen Fall konstruieren können, der fragmentiert … nur
ist der bei wirklich zufälliger Belegung nicht sehr praxisrelevant.
Wie viel das „ein wenig mehr“ tatsächlich ist, müsste man mit einer
Monte-Carlo-Simulation ermitteln (die natürlich auch eine
Restwahrscheinlichkeit enthält, dass alle Ressourcen aufgebraucht sind).
Rein vom Gefühl her denke ich, dass man schon mit ca. 20 % an Reserve
eine ziemlich hohe Sicherheit hat, dass es funktioniert.
Aber wie geschrieben, das setzt voraus, dass die Ereignisse (also wann
wird welche Blockgröße benötigt und wie lange) tatsächlich zufällig
verteilt sind.
Vergessen habe ich natürlich was ich benutze...
ATmega2560 mit Atmel Studio 7 und AVR GCC.
Sicher kann ich die 64 Bate auch einfach dauerhaft "verballern" nur
"schön" finde ich es nicht und wer weiß was kommt, vielleicht sind es in
einem anderen Projekt mal deutlich mehr?
Jörg W. schrieb:> malloc() gibt's auch für AVRs
Da werde ich mal schauen ob ich da was finde... DANKE!
AVRli .. schrieb:> Danke, innerhalb einer Funktion ist bekannt.> Mein Problem ist dabei, dass die Aktionen nicht alle in eine einzelne> Funktion hintereinander ablaufen.>> Die Daten werden via UART eingesammelt, das Main muss in der Zeit weiter> laufen.>> Zur Zeit habe ich die 64 Werte global im Main definiert, das
(Oben waren es 34)
> funktioniert schon mit dem Nachteil der Verschwendung, wenn ich den Teil> nicht benötigt wird.
Das heißt du brauchst den Platz dann zwischendurch für was anderes? Ist
sichergestellt, dass den Platz nie für zwei Sachen gleichzeitig
brauchst?
Für das, was du beschreibst, ist, wie schon erwähnt wurde, malloc()
gedacht:
1
uint16_t*werte=malloc(64*sizeof*werte);
Dann kannst du über diesen Zeiger dein Array benutzen, auch über
Funktionsgrenzen hinweg. Und wenn du fertig bist, machst du:
1
free(werte);
Jörg W. schrieb:> Fragmentierung gibt's erstens sowieso nur, wenn man unterschiedlich> große Blöcke braucht und dann verschieden wieder freigibt. Klang mir> beim TE nicht unbedingt so, aber vielleicht hat er's auch nur nicht ganz> verständlich formuliert.
Naja, er will den Speicher an der Stelle dynamisch allokieren, damit er
ihn an anderen Stellen für was anderes verwenden kann. In was für
Blockgrößen dort allokiert wird, ist aber völlig unbekannt.
Wenn er immer nur genau einen 34 oder 64 Werte großen allokiert und dann
wieder deallokiert, bringt ihm das dynamische Speichermanagement ja eher
wenig.
> Zweitens ist Fragmentierung vor allem dann ein Problem, wenn die> Belegung und Freigabe nicht wirklich sehr zufällig ist. Sofern das> tatsächlich guter Zufall ist und stets im Mittel „ein wenig mehr“ an> Speicher da ist als insgesamt nötig ist, dann klappt das mit dem> Sich-selbst-Defragmentieren ganz gut.
Auf einem PC mit Gigabytes an RAM ist das kein Problem. Auf einem AVR,
der wenn's hoch kommt ein paar kB hat, sieht das aber etwas anders aus.
Rolf M. schrieb:> Auf einem AVR, der wenn's hoch kommt ein paar kB hat, sieht das aber> etwas anders aus.
Die Frage ist nicht die absolute Speichergröße, sondern wie viel davon
in „Spitzenzeiten“ etwa maximal alloziert wird. Solange das gut unter
der freien RAM-Größe ist, sortiert sich ein stochastisch arbeitendes
System mit hoher Wahrscheinlichkeit (aber natürlich nicht mit 100%iger
Sicherheit!) wieder selbst passend. Ist vom Prinzip her ein bisschen wie
das CSMA/CD beim Ethernet: solange dort noch ein wenig „Luft“ auf dem
Medium ist, ist die Wahrscheinlichkeit hoch, dass mein Datenpaket auf
den Draht darf, auch, wenn es eben nicht garantierbar ist. Konkurrent
Token Ring garantierte die Verfügbarkeit zwar – aber einen wirklichen
Vorteil brachte das erst bei weit über 90 % Medienauslastung.
Mittlerweile redet keiner mehr über den „Toten Ring“ …
Das oft empfohlene „allozier doch genügend statischen Speicher vorab“
würde in vergleichbaren Szenarien natürlich schon viel eher an seine
Grenze stoßen, denn dort muss ich meine Allokationseinheit an der Größe
der maximalen Einzelanforderung ausrichten (und dann <N> solcher
Einheiten vorab allozieren). Alle Anforderungen, die weniger als das
brauchen, verschwenden daher Speicher. Im Mittel wird dann also die
Verschwendung größer sein, oder eben anders gesagt, die
Ressourcenverknappung bereits viel eher eintreten.
Statische Speicherallokation ist pessimistisch: Ich verschwende Speicher
für die Garantie, dass mein Bedarf immer gedeckt ist.
Dynamischer Speicher ist optimistisch: Ich erlaube die Mehrfachnutzung
des vorhandenen Speichers in der Hoffnung, meinen Bedarf trotzdem fast
immer decken zu können.
In beiden Fällen muss ich meinen Bedarf sinnvoll abschätzen können. Aber
nur im dynamischen Fall muss ich auch damit umgehen können, dass malloc
jederzeit fehlschlagen kann. Auch wenn pathologische Fälle statistisch
nicht auftreten, kann ein bösartiger Angreifer sie oft auch absichtlich
erzeugen. Sowas lässt sich nur schwer ausschließen.
Unions sind kein allgemeiner Ersatz für dynamischen Speicher.
Jörg W. schrieb:> A. S. schrieb:>> Eine Lösung für sowas ist dynamische Speicherallokierung. Dafür brauchst>> du aber ein Betriebssystem,>> Wie kommst du denn auf diesen Trichter?>> malloc() gibt's auch für AVRs nun schon seit geraumer Zeit – ganz ohne> Betriebssystem.
bitte nur ganze Sätze zitieren ;)
Offensichtlich gibt's für den AVR einen Manager fertig in einer
Bibliothek. Gut für alle, die sich trauen, so etwas zu benutzen.
Genauso könnte man dem Threadersteller übrignes "union" empfehlen. Damit
kann man Speicher doppelt benutzen - nur eben nicht gleichzeitig.
Ist vielleicht sogar weniger gefährlich, weil es den Erfinder zwingt ein
deterministisches System zu bauen...
Meine grundsätzliche Empfehlung wäre es, stochastische Unsicherheiten
unter Kontrolle zu bringen, bevor man auch noch mit knappem Speicher
kämpfen muss.
Jörg W. schrieb:> Anders gesagt, dynamische Speicheranforderungen, gleich wie sie bedient> werden, müssen immer eine Strategie haben, wie sie mit> Ressourcenknappheit umgehen.
Ich glaube in 99,x % der Fälle ist das stumpfes Abstürzen. Bei Linux
auch gerne in Zeitlupe.
AVRli .. schrieb:> - lösche die 34 uint_16 Variablen wieder
wer oder was soll denn den "freien Speicher" in er Zwischenzeit Nutzen?
Soll er Tamagotchi II / Moorhuhn extended edition spielen bis du ihn
wieder brauchst ;-) ?
A. S. schrieb:> Genauso könnte man dem Threadersteller übrignes "union" empfehlen.
Mir geht es primär um den Zustand das ich für eine Routine, die vlt.
einmal in der ganzen Lebenszeit der Platine genutzt wird, der Speicher
"weg ist". Klar kann man argumentieren, ist doch egal.
Auch wenn sich einige darüber lustig machen, mich interessiert wie man
Resourcen sparend programmiert.
malloc(); scheint für dene inen Fall auch nicht das richtige zu sein.
"Union" ist auch neu für mich, das schau ich mir jetzt mal an.
Danke für alle ernsten Antworten!
Gruß AVRli...
OK, mit dynamischer Speicherverwaltung kannst du den RAM verwalten. Die
Speicherverwaltung selbst verballert aber ziemlich viel Programmspeicher
und Laufzeit.
Auf einem Mikrocontroller würde ich eine dynamische Speicherverwaltung
daher nur in Ausnahmefällen benutzen, wenn es gar nicht anders geht.
Je nach Norm ist eine dynamische Speicherverwaltung bei
sicherheitsrelevanter Programmierung (sicher im Sinne safety) sogar
verboten.
AVRli .. schrieb:> Sicher kann ich die 64 Bate auch einfach dauerhaft "verballern" nur> "schön" finde ich es nicht
Es gibt fürs Programmieren keinen Schönheitspreis.
Du kannst natürlich eine Union erstellen, um die 64 Byte noch
anderweitig zu verwenden. Nur mußt Du Dir dann auch 101%-ig sichern
sein, daß sich beide Anwendungen nie in die Quere kommen. In der Regel
handelst Du Dir damit noch viel mehr Ärger ein, als mit 0,8%
"Verschwendung".
Barrex schrieb:> Die> Speicherverwaltung selbst verballert aber ziemlich viel Programmspeicher> und Laufzeit.
Gut Laufzeit wäre vlt. noch vernachlässigbar in meinem Fall,
Programmspeicher hingegen ist natürlich auch "kostbar".
Ich lasse es mit malloc();
Peter D. schrieb:> Es gibt fürs Programmieren keinen Schönheitspreis.
Das glaube ich gerne! :-D
Peter D. schrieb:> Du kannst natürlich eine Union erstellen, um die 64 Byte noch> anderweitig zu verwenden. Nur mußt Du Dir dann auch 101%-ig sichern> sein, daß sich beide Anwendungen nie in die Quere kommen.
Das bin ich mir.
Ich brauche die Union wenn dann nur im Setup und da kann immer nur ein
Parameter zur gleichen Zeit festgelegt werden.
Ich werde es mit Union einfach mal probieren, habe ich noch nie gemacht.
Ist mal was neues...
Danke für Eure Vorschläge und Erklärungen zum Für und Wieder.
Gruß AVRli...
Auch bei der Funktionsvariante, d.h. temporaeres Speichern auf dem
Stack, braucht man Speicherplatz. In diesem Fall auf dem Stack. Ich
vermeide es immer, groessere Datenmengen auf den Stack zu packen, man
(ich) hat den Stackbedarf sowieso nicht im Blick. Die 60 Variablen
zusaetzlich auf dem Stack sind bei der Initalisierung, wenn es keine
tiefen Funktionsverschachtelungen gibt, vielleicht aber kein Problem.
Der Union-Vorschlag scheint mir nicht schlecht, aber das wird man nur
machen, wenn der Speicher tatsaechlich knapp wird. Mit dynamischem
Speicher zu arbeiten ist aber auch so eine Sache.
Im Grunde sind das halt alles Kompromisse, auf die man erst dann
zugreifen sollte, wenn die Anwendung das verlangt.
AVRli .. schrieb:> Mir geht es primär um den Zustand das ich für eine Routine, die vlt.> einmal in der ganzen Lebenszeit der Platine genutzt wird, der Speicher> "weg ist". Klar kann man argumentieren, ist doch egal.
Wie gesagt, im Normalfall würde man in so einem Fall die Variablen in
der Routine auf dem Stack anlegen, dann verschwinden sie automatisch am
Ende der Funktion. Das geht bei dir nicht, was ich seltsam finde.
Eventuell könnte man die Programmstruktur anpassen, dass es so geht.
Ansonsten gilt: Für ungenutzten Speicher bekommst du kein Geld zurück.
Wenn dein restliches Programm also 64 Bytes übrig lässt, dann kannst du
die ruhig fürs Setup reservieren - dann sind sie wenigstens manchmal zu
was nütze.
S. R. schrieb:> Statische Speicherallokation ist pessimistisch: Ich verschwende Speicher> für die Garantie, dass mein Bedarf immer gedeckt ist.
Nur, wenn du ein dynamisches Problem hast (nur dann hat eine dynamische
Speicherzuweisung überhaupt Sinn), dann weißt du nicht vorab, wie viele
Anforderungen ggf. auf einmal eintreffen werden. Du hast folglich auch
da keinerlei Garantie, dass der Bedarf immer gedeckt ist.
Die einzige Garantie, die du hast ist, dass dir kein malloc() ein NULL
zurückgeben kann. Stattdessen sind dann halt die Slots deines
vorbelegten Arrays alle … der Effekt bleibt der gleiche. Darum schrieb
ich ja, wenn man so ein Problem hat, dann braucht man immer eine
Strategie, wie man damit umgeht, dass man die ankommenden Anforderungen
jetzt gerade doch nicht bedienen kann. Das kann sein, dass man jetzt
einfach am besten neu startet, kann auch sein, dass man eben die nicht
bedienbaren Anforderungen ignorieren muss. Vielleicht kann man ja
demjenigen, der die Anforderung initiiert hat, noch einen Hinweis
darüber geben. Hängt sehr von der Situation ab.
A. S. schrieb:> bitte nur ganze Sätze zitieren ;)
Hätte auch nichts geholfen, dieweil hier nicht zutreffend. Daher hatte
ich ihn weggelassen.
Mach schrieb:> Auch bei der Funktionsvariante, d.h. temporaeres Speichern auf dem> Stack, braucht man Speicherplatz. In diesem Fall auf dem Stack.
Im Gegensatz zu malloc() hat man dort übrigens auf einem kleinen
Controller (ohne MPU) keinerlei Garantie, dass der Stack dann nicht
ggf. mit den statischen Daten kollidiert. Das ist noch viel fataler als
ein malloc(), das einem wenigstens noch sauber mit NULL signalisieren
kann, dass jetzt der Speicher alle ist. Solcherart Bugs zu suchen macht
alles andere als Spaß, denn sie treten nur aller Jubeljahre auf und
werden erst viel später bemerkt (nämlich dann, wenn mal wieder jemand
die zerstörten statischen Daten braucht).
AVRli .. schrieb:> Gut Laufzeit wäre vlt. noch vernachlässigbar in meinem Fall,> Programmspeicher hingegen ist natürlich auch "kostbar".
Ich vermute zwar nicht, dass malloc() für dich wirklich eine großartige
Lösung ist, aber so extrem ist der Speicherbedarf nun auch wieder nicht:
ca. 0,5 KiB. Für eine einmalige Aktion sicher nicht sinnvoll, wenn man
es wirklich braucht, in der Regel kompakter als irgendwelche Versuche
eines eigenen Speichermanagements – und mittlerweile wahrscheinlich bis
zum letzten Bit debuggt. :-)
Jörg W. schrieb:> Nur, wenn du ein dynamisches Problem hast (nur dann hat eine dynamische> Speicherzuweisung überhaupt Sinn), dann weißt du nicht vorab, wie viele> Anforderungen ggf. auf einmal eintreffen werden. Du hast folglich auch> da keinerlei Garantie, dass der Bedarf immer gedeckt ist.
Das meinte ich mit "den Bedarf sinnvoll abschätzen". Wobei mein
Bauchgefühl sagt, dass die meisten Probleme für kleinere Mikrocontroller
(d.h. der AVR-Klasse) ohnehin nicht dynamisch sind. Ich muss vorher
festlegen, wieviele Anfragen ich verarbeiten können will und was mit dem
Rest passiert, dafür bekomme ich aber die Garantie, dass das auch
(unabhängig vom restlichen Systemzustand) klappt.
Wenn in meinem Code ständig Puffer alloziiert und freigegeben werden,
dann habe ich nur eine statistische Abschätzung, dass ich N Anfragen
bearbeiten kann. Wenn das Timing ungünstig ist, können es auch mal
weniger sein. Dafür ist N wahrscheinlich größer als im statischen Fall.
Es ist immer eine Kompromisslösung. Bisher bin ich mit statischen
Allokationen ausgekommen, oder hatte malloc()-Aufrufe ausschließlich vor
der Hauptschleife.
S. R. schrieb:> Bisher bin ich mit statischen Allokationen ausgekommen, oder hatte> malloc()-Aufrufe ausschließlich vor der Hauptschleife.
Ich habe malloc() auch auf AVRs schon benutzt (klar, die Implementierung
in der avr-libc ist ja von mir ;), aber auf aktuellen kommerziellen
ARM-Projekten hat die Umstellung von einer vormals handgestrickten,
vor-allozierten Lösung auf striktes malloc() für alles, was "on demand"
ist, einen deutlich klareren, saubereren und stabileren Code bewirkt.
Klar, die Teile sind nun keine kleinen Controller mehr (aber der
ATmega2560 ist das auch nicht).
Man sollte sich aber immer vergegenwärtigen, dass UNIX (und damit auch
malloc()) auf einer PDP-11 mit gerade mal 2 x 64 KiB Adressraum (je
einmal für Befehle und Daten) groß geworden ist.
Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang