Forum: Mikrocontroller und Digitale Elektronik C Speicherverwaltung


von Horst (Gast)


Lesenswert?

Hallo zusammen,

bisher habe ich mich nicht so recht an die Speicherverwaltung mit malloc 
etc. herangetraut. Aktuell aber habe ich ein Problem bei dem ich 
warscheinlich nicht drum rum komme um nicht arg viel Speicher zu 
verschwenden.

Folgende Ausgangslage und wie ich das bisher gelöst hätte:

Über die UART Schnittstelle kommen Telegramme herein deren Länge von 8- 
270Byte variiert. In der ISR werden die einzelnen Telegrammbytes in ein 
Buffer Array gespeichert und nach Telegrammende das gesamte Telegramm in 
einen Telegrammbuffer (+ACK an den Absender). In der Hauptroutine werden 
diese Telegramme im Telegrammbuffer der Reihe nach abgearbeitet, diese 
Abarbeitung kann aber Längere Zeit in Anspuch nehmen daher der Einsatz 
eines Bufferspeichers. Wenn der Bufferspeicher voll ist sollen keine 
neuen Telegramme mehr Angenommen werden (NACK an den Absender)

Datenstrukur:
1
#define MAX_TELLEN 270
2
#define MAX_TELCNT 10
3
4
struct _Telegram
5
 {
6
   uint8_t data[MAX_TELLEN];
7
   uint16_t len;
8
 };
9
10
struct _Telegrambuffer
11
{
12
  struct _Telegram Telegram[MAX_TELCNT];
13
  uint8_t WritePtr;
14
  uint8_t ReadPtr;
15
}Telegrambuffer;
16
17
void UART_ISR()
18
{
19
  ....
20
21
  //Empfangense Byte Speichern
22
  Telegrambuffer.Telegram.data[Telegrambuffer.Telegram.len++]=c;
23
24
  ....
25
26
  // Wenn Adressierung ok und Telegrammbuffer nicht voll dann ACK senden
27
  ....
28
29
  //nach Empfang des letzten Telegramm Bytes
30
  Telegrambuffer.WritePtr++;
31
32
  ....
33
}
34
35
void main()
36
{
37
  while(1)
38
   {
39
     if(Telegrambuffer.ReadPtr!=Telegrambuffer.WritePtr)
40
       {
41
         //Telegrammverarbeitung
42
         ....
43
44
         Telegrambuffer.ReadPtr++;
45
       }
46
   }
47
}


Fuktionieren würde das alles so aber da zu über 80% die Telegramme 
kürzer als 20Byte sein werden ist der Rest des Telegrambuffers 
verschwendeter RAM und daher ist je nach Größe des Arbeitsspeichers die 
Anzahl der möglichen gepufferten Telegramme unnötig beschränkt.

Jetzt weiß ich das mit realloc() calloc) etc. dynamisch zur Laufzeit 
Speicher reserviert bzw. wieder frei gegeben werden kann. Vorteil wäre 
das nur soviel Speicher belegt wird wie die einzelnen Telegramme 
tatsächlich brauchen und damit die Anzahl der möglichen gepufferten 
Telegramme drastisch steigen würde.

Im Prinzip reserviere ich mir bei Telegrammstart vorsorglich zb. 20Byte 
und erweitere bzw. kürze es bei Bedarf entsprechend. (ab Byte 8 weiß ich 
die genaue Länge des Datentelegramms) Ist das in einer ISR überhaupt 
möglich, günstig (ralloc() Aufrufe)?
Wenn ich den freien Speicher zur Laufzeit auch irgendwie rausfinde 
könnte man die Anzahl der möglichen zwischengespeicherten Telegramme je 
nach Auslastung variieren.

Da ich damit noch keine Erfahrung habe, frage ich mich ob das 
prinzipiell möglich ist oder ob ich mich da auf dem Holzweg befinde? 
Wenn möglich wie könnte so etwas aussehen, hat jemand vielleicht schon 
was änliches gemacht?

Bin über jede Hilfe dankbar :)

von thomas s. (Gast)


Lesenswert?

einen circularbuffer nehmen mit 270byte größe und die telegramme 
herausnehmen und verarbeiten

von Horst (Gast)


Lesenswert?

Das wäre ja genau die Variante die Speichertechnisch nicht gerade 
effektiv ist!

von Markus M. (mark_m)


Lesenswert?

Wie sieht den das Telegramm aus? Du müsstest zuerst die Telegrammlänge 
senden, damit Du mit dieser Information den Dynamischen Speicher anlegen 
kannst. Kennst Du die Länge vor her nicht -> Worst Case.

Bei der dynamischen Speicherverwaltung ist nicht garantiert, dass immer 
genügend Speicher verfügbar ist. Wie willst Du diesen Fall behandeln?

Du schreibst ja, dass Du erst das ganze Telegramm empfängst und danach 
dieses verarbeitest. Falls ich es richtig verstanden haben, reicht ein 
Buffer mit 270 Byte Länge.

Für den Fall das Empfangen und Verarbeiten parallel geschehen soll, 
könntest Du zwei Buffer anlegen. Ist der eine Buffer mit einem 
kompletten Telegramm gefüllt, tauschst Du die Zeiger auf die Buffer. Vor 
dem tausch muss die Verarbeitung natürlich auch abgeschlossen sein. Der 
Buffer der gerade noch Empfangsbuffer war, wird nun Verarbeitungsbuffer 
und der Verarbeitungsbuffer wird zum Empfangsbuffer.

Welchen MC verwendest Du? Wie viel RAM hast Du zur Verfügung?

Grüsse

von Uwe (Gast)


Lesenswert?

> Das wäre ja genau die Variante die Speichertechnisch nicht gerade
> effektiv ist!
Wieso ? Du schreibst immer in diesen Puffer rein und merkst dir die 
Aktuelle Position in einem Zähler. Nachdem ein Telegram komplett ist 
merkst du dir die position in einem array (entweder Index oder Pointer) 
für den Anfang des neuen Telegramms und sendest ein ACK. Du hast einen 
weiteren Zähler für den, der die Telgramme verarbeitet und die pointer 
im Array invalidiert als abgearbeitet usw.

von Karl H. (kbuchegg)


Lesenswert?

> Jetzt weiß ich das mit realloc() calloc) etc. dynamisch zur Laufzeit
> Speicher reserviert bzw. wieder frei gegeben werden kann. Vorteil wäre
> das nur soviel Speicher belegt wird wie die einzelnen Telegramme
> tatsächlich brauchen und damit die Anzahl der möglichen gepufferten
> Telegramme drastisch steigen würde.

Nachteil ist, dass du keinerlei Garantie hast, dass du diesen Speicher 
auch kriegst.
Du kannst rein rechnerisch 500 Bytes frei haben und trotzdem geht eine 
Allokierung von 40 Bytes schief.
Warum? Weil es möglich ist, dass du zwar in Summe 500 Bytes hast, aber 
keine einzige zusammenhängende freie Lücke im Speicher, die 40 Bytes 
umfasst. Du hast zb 50 Stück jeweils 10 Byte große Speicherbereiche im 
Speicher frei.


Wenn dieser Speicher, um den es hier geht, der einzige ist, den du 
dynamisch verwalten möchtest, dann lass es so. Besser du hast 
garantierte Zahlen als du hast 'möglicherweise', 'vielleicht' den Platz 
um 3 oder 4 zusätzliche Telegramme 'unter Umstände' 'eventuell' 
speichern zu können, ehe du den Sender bremsen musst.

von Falk B. (falk)


Lesenswert?

@ Horst (Gast)


>Jetzt weiß ich das mit realloc() calloc) etc. dynamisch zur Laufzeit
>Speicher reserviert bzw. wieder frei gegeben werden kann. Vorteil wäre
>das nur soviel Speicher belegt wird wie die einzelnen Telegramme
>tatsächlich brauchen und damit die Anzahl der möglichen gepufferten
>Telegramme drastisch steigen würde.

Und was machst du mit dem "gesparten" RAM? Auf so einem kleinen 
Mikrocontroller mit einem FESTEN Programm fast gar nichts. Denn es gibt 
keine unvohersehbaren Anwendungen, die plötzlich gestartet werden und 
viel RAM brauchen könnten. Deine Anwendung ist klein, fest und 
übersichtlich.

>prinzipiell möglich ist

Ja, aber

> oder ob ich mich da auf dem Holzweg befinde?

Auch ja ;-) Dein Malloc brungt nur Stress und Probleme und keinen 
Vorteil. Einzig und allein dann, wenn du zwischenzeitlich für andere 
Funktionen kurzzeitig viel RAM brauchst. Dann darf aber während dieser 
Zeit GARANTIERT kein langes Telegramm ankommen.

von Karl H. (kbuchegg)


Lesenswert?

> In der Hauptroutine werden diese Telegramme im Telegrammbuffer
> der Reihe nach abgearbeitet, diese Abarbeitung kann aber Längere
> Zeit in Anspuch nehmen

Arbeite lieber daran, diese Abarbeitung zu beschleunigen.
Im Moment hast du deinen Buffer auf 10 Telegramme eingestellt. Wie oft 
kommt es vor, dass diese 10 Telegramme nicht ausreichen? Wenn du die 
Abarbeitung um ein paar Prozent beschleunigen kannst, wie oft reichen 
<dann> diese 10 Telegramme nicht mehr aus?
Hast du das schon rausgemessen und festgestellt?

Dynamische Speicherallokierung hat nicht nur das Problem der 
Fragmentierung (wie in meinem letzten Post angesprochen), sondern auch 
das Problem, dass sie relativ aufwändig ist. D.h. durch den Einsatz der 
dynamischen Allokierung (das muss ja auch verwaltet werden), zwickst du 
dir zusätzliche Laufzeit auf, die als Folge jetzt wieder dazu führen 
kann, dass die Abarbeitung der Telegramme langsamer wird und wo vorher 
10 Buffer-Slots gereicht haben, reichen jetzt 11 oder 12 nicht mehr.

von Karl H. (kbuchegg)


Lesenswert?

Was man vielleicht in Erwägung ziehen könnte, das ist eine Mischung aus 
'Short-Telegram' und 'Long-Telegram'. Dann hast du zb 30 
Short-Telegramme vorrätig und nur 5 Long-Telegramme.

In den Telegrammbuffer kommt anstelle des Struktur-Arrays ein Pointer 
Array rein, zusammen mit einer Kennung, auf welchen Typ von Telegram der 
Pointer zeigt.


Aber: Das wird vergleichsweise immer noch aufwändig. Denn jetzt brauchst 
du natürlich getrennte Verwaltungen dafür, welche Short- bzw. 
Long-Telegramme gerade noch frei sind. Aus dem jeweiligen Pool muss 
eines 'beschlagnahmt' werden und sein Pointer in den Telegrammbuffer 
eingetragen werden. Selbiges dann natürlich in umgekehrter Richtung, 
wenn das Telegramm nicht mehr gebraucht wird.

Zumindest das Problem der Speicherfragmentierung kriegt man so in den 
Griff. Aber die Laufzeitverzögerung, und damit die Tatsache, dass du 
u.U. auf einmal mehr Telegramme buffern musst, dieses 'Problem' 
existiert immer noch.


Daher: als erstes mal messen, wie oft eigentlich dieser Fall auftritt, 
dass die 10 vorhandenen Telegramme nicht ausreichen. Wenn der so gut wie 
nie auftritt, dann optimierst du etwas, was du überhaupt nicht 
optimieren brauchst.

> Fuktionieren würde das alles so aber da zu über
> 80% die Telegramme kürzer als 20Byte sein werden

Darauf kommts nicht an

> ist der Rest des Telegrambuffers verschwendeter RAM

für nicht benutztes SRAM kriegst du von Atmel kein Geld zurück. Und dem 
µC ist es auch egal, ob du jetzt SRAM Zellen benutzt oder nicht benutzt.

Entscheidend ist einzig und alleine, inwiefern dir diese Limitierung auf 
10 gebufferte Telegramme tatsächlich den Datenfluss behindert.

von Horst (Gast)


Lesenswert?

Markus M. schrieb:
> Wie sieht den das Telegramm aus? Du müsstest zuerst die Telegrammlänge
> senden, damit Du mit dieser Information den Dynamischen Speicher anlegen

Kenne die Länge des Telegramms leider erst ab Byte 8, dort ist diese 
Information vorhanden, daran kann ich auch nichts ändern. Darum würde 
ich ja einfach Hausnummer 20 Byte allocieren und danach auf die exakte 
Länge anpassen (wenn ein zusammenhängender Block mit der neuen 
Byteanzahl nicht auf der aktuellen Position verfügbar ist muss realloc() 
die paar bytes zuvor umkopieren oder? deshalb die Frage wegen ISR!)


Markus M. schrieb:
> Bei der dynamischen Speicherverwaltung ist nicht garantiert, dass immer
> genügend Speicher verfügbar ist. Wie willst Du diesen Fall behandeln?

dann wird das Telegramm nicht angenommen (NACK) der Sender versucht es 
später wieder und dann ist vielleicht wieder speicher frei....

Markus M. schrieb:
> Du schreibst ja, dass Du erst das ganze Telegramm empfängst und danach
> dieses verarbeitest. Falls ich es richtig verstanden haben, reicht ein
> Buffer mit 270 Byte Länge.

Nein, im worst case könnten einige Telegramme in der Warteschlage zur 
Bearbeitung stehen, wenn ich dafür einen Buffer mit fixen 270Byte Länge 
verwende könnten zb. bei 10 Telegrammen 2,7kB flögen gehen obwohl die 10 
Telegramme evtl. nur 10x20Byte also 200Byte bräuchten.....


Markus M. schrieb:
> Für den Fall das Empfangen und Verarbeiten parallel geschehen soll

natürlich, aber die Verarbeitung kann eben längere Zeit dauern und es 
kommen dennoch neue Telegramme daher. Je effektiver ich den Speicher 
verwalte desto mehr Telegramme kann ich in dieser Zeit zwischenspeichern 
und danach verarbeiten!

Markus M. schrieb:
>Welchen MC verwendest Du? Wie viel RAM hast Du zur Verfügung?

Keinen konkreten, wird auf mehreren Controllern laufen und daher wird 
mal mehr und mal weniger Ram zur verfügung Stehen

von Horst (Gast)


Lesenswert?

ui schon wieder soviele Antworten, komm ich ja gar nicht nach :)

Das es nicht leicht ist verstehe ich, daher habe ich es auch immer 
vermieden. Dachte jetzt nur weil es auf unterschiedlichen Controllern 
laufen wird könnte man da etwas optimieren.

Kontkret läuft es noch nicht daher kann ich noch nicht wirklich sagen 
wieviele Telegramme Platz haben müssten, wird aber auch sehr 
unterschiedlich sein weil die Aufgesetzten Applicationen sehr 
unteschiedliche aufgaben erfüllen.

Sind eben erst Grundsätzliche Überlegungen aber danke schon mal für die 
rege Beteiligung :)

von Markus M. (mark_m)


Lesenswert?

Da hast Du aber eine ganze Menge Unbekannte! Es ist keine schlechte Idee 
diese im voraus zu minimieren.

Ein intelligentes Speichermangement kostet auch Speicher. Ob Du etwas 
gewinnst gegenüber einer statischen Speichernutzung wirst du wohl am 
Ende der Arbeit sehen. Aber lohnt sich der Aufwand? Nicht das KISS 
Prinzip aus den Augen verlieren.

Weiter oben wird ja schon die Speicherfragmentierung angesprochen. Auf 
einem MC ist es besser die Kontrolle selbst zu übernehmen auch über den 
Speicher.

Grüsse

von Karl H. (kbuchegg)


Lesenswert?

Na ja.
Wenns denn unbedingt sein muss

1
struct _Telegram
2
{
3
  uint8_t*          data;
4
  uint16_t          len;
5
  struct _Telegram* pNext;
6
};
7
8
struct _Telegram* frstTelegram = NULL;
9
struct _Telegram* lastTelegram = NULL;
10
11
void UART_ISR()
12
{
13
  static uint8_t  data[270];
14
  static uint16_t dataLen;
15
  ....
16
17
  //Empfangense Byte Speichern
18
  data[dataLen++] = c;
19
20
  ....
21
22
  // Wenn Adressierung ok und Telegrammbuffer nicht voll dann ACK senden
23
  ....
24
25
  //nach Empfang des letzten Telegramm Bytes
26
  {
27
    struct _Telegram* pNewTelegram = malloc( sizeof( struct_Telegram ) );
28
    pNewTelegram->data = malloc( dataLen );
29
30
    memcpy( pNewTelegram->data, data );
31
    pNewTelegram->len = dataLen;
32
    pNewTelegram->pNext = NULL;
33
34
    if( frstTelegram == NULL )
35
      frstTelegram = pNewTelegram;
36
    else
37
      lastTelegram->pNext = pNewTelegram;
38
39
    lastTelegram = pNewTelegram;
40
41
    dataLen = 0;
42
  }
43
}
44
45
void main()
46
{
47
  while(1)
48
   {
49
     if( frstTelegram )
50
     {
51
       // sollte unter Interruptsperre laufen!
52
       struct _Telegram* thisTelegram = frstTelegram;
53
       frstTelegram = frstTelegram->pNext;
54
       if( !frstTelegram )
55
         lastTelegram = NULL;
56
57
       // ab hier sind Interrupts wieder offen
58
59
       //Telegrammverarbeitung
60
       // mach was mit thisTelegram
61
62
       // und freigeben
63
       free( thisTelegram->data );
64
       free( thisTelegram );
65
     }
66
  }
67
}


Ich hab das jetzt mal so runtergeschrieben. Bis auf Tippfehler sollte da 
nichts mehr drinnen sein. Die Allokierung und Freigabe ist einfach eine 
verlinkte Liste von Telegrammen, die am hinteren Ende wächst, während 
die Hauptschleife sich ein Telegram nach dem anderen vom vorderen Ende 
holt, abarbeitet und freigibt.

von Karl H. (kbuchegg)


Lesenswert?

Horst schrieb:

> Kontkret läuft es noch nicht daher kann ich noch nicht wirklich sagen
> wieviele Telegramme Platz haben müssten, wird aber auch sehr
> unterschiedlich sein weil die Aufgesetzten Applicationen sehr
> unteschiedliche aufgaben erfüllen.

Dann solltest du das erst mal mit der einfachsten Implementierung in 
Erfahrung bringen.

Man 'optimiert' nicht etwas, von dem man nicht weiß, ob es überhaupt ein 
Problem ist.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich würde einen möglichst großen Ringpuffer für alle empfangenen
Telegramme statisch anlegen. Die einzelnen Telegramme werden darin
sequenziell abgelegt und jeweils durch ein spezielles Endezeichen (bspw.
'\0') abgeschlossen. Das funktioniert, weil die Verarbeitungsroutine die
Telegramme ebenfalls sequenziell liest.

In diesem Ringpuffer passen je nach Bedarf viele kleine oder wenige
große Telegramme hinein, und es wird im Gegensatz zur malloc-Lösung bis
auf die Endezei- chen kein Platz für Verwaltungsinformation benötigt.

Und es gibt keinerlei Probleme mit Speicherfragmentierung, die du evtl.
bei der Verwendung von malloc hättest.

Und rechenzeiteffizient ist diese Methoden noch dazu.

Um die Größe dieses Ringpuffers festzulegen, musst du nachschauen,
wieviel RAM alle anderen statischen Variablen zusammen benötigen (das
steht im Map-File des Linkers oder kann bei den GNU-Tools mit dem
size-Befehl ermittelt werden). Zusätzlich musst du den Stackverbrauch
abschätzen oder durch Testläufe herausfinden. Im verbleibenden Speicher
kann sich dann der Ringpuffer breit machen.

Ob der Puffer voll ist, kannst du wie üblich daran erkennen, dass der
Schreibindex den Leseindex einholt. Ist dies der Fall, schickts du das
NACK und entfernst das letzte (nicht vollständige) Telegramm aus dem
Puffer, indem zu den Schreibindex auf den Anfang dieses Telegramms
zurücksetzt. Das geht am einfachsten dadurch, dass du noch einen dritten
Index einführst, der immer auf den Anfang des aktuell empfangenen
Telegramm zeigt.

von Hmm (Gast)


Lesenswert?

>Das wäre ja genau die Variante die Speichertechnisch nicht gerade
>effektiv ist!

Mich würde mal interessieren warum Horst dieser Meinung ist.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Hmm schrieb:
>>Das wäre ja genau die Variante die Speichertechnisch nicht gerade
>>effektiv ist!
>
> Mich würde mal interessieren warum Horst dieser Meinung ist.

Naja, wenn er für jedes Telegramm 270 Bytes reserviert, aber viele
Telegramme nur 20 Bytes lang sind, bleiben für jedes dieser Telegramme
250 Bytes ungenutzt.

von Hmm (Gast)


Lesenswert?

>Naja, wenn er für jedes Telegramm 270 Bytes reserviert, aber viele
>Telegramme nur 20 Bytes lang sind, bleiben für jedes dieser Telegramme
>250 Bytes ungenutzt.

Wenn aber im Worst-Case soviele Telegramme auftreten können, ist der 
Ringbuffer immer noch kleiner als malloc/free und das nicht genutzte RAM 
kann er ja sowieso nicht anderweitig benutzen, wenn man voraussetzt das 
der Worst-Case jederzeit auftreten kann.

Überzeugt mich noch nicht davon, warum man die Ringpuffer-Lösung 
ablehnen sollte.

von Horst (Gast)


Lesenswert?

Danke an alles bisherigen Antworten, habe jetzt einigen neue Sichtweisen 
aufgezeigt bekommen, besonders die Idee von Yalu X. scheint mir ein 
Interessanter Weg zu sein da zwar der reservierte Speicher fix ist, 
dieser aber optimal ausgenutzt werden kann.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Hmm schrieb:
> Wenn aber im Worst-Case soviele Telegramme auftreten können, ist der
> Ringbuffer immer noch kleiner als malloc/free und das nicht genutzte RAM
> kann er ja sowieso nicht anderweitig benutzen,

Doch, bei richtiger Speicherorganisation kann der Puffer bei kleinen
Telegrammen eine größere Anzahl davon aufnehmen, was die Häufigkeit der
Pufferüberläufe und der dadurch ausgelösten Sendewiederholungen
reduziert.

@Horst:

Mir ist gerade noch eingefallen, dass du in meinem Vorschlag von oben
die Endezeichen nach den Telegrammen auch weglassen kannst, da die
jeweilige Länge ja bereits in Telegrammdaten enthalten ist. Damit hast
du praktisch keine Speicheroverhead im Puffer.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Horst schrieb:
> Da ich damit noch keine Erfahrung habe, frage ich mich ob das
> prinzipiell möglich ist oder ob ich mich da auf dem Holzweg befinde?

Ja, ist es.

Lass dich mal nicht von den Unkenrufern hier entmutigen: solange
dein Problem wirklich stochastisch ist (also die langen und kurzen
Telegramme zufällig verteilt reingeflogen kommen) und solange du
gegenüber dem zu erwartenden maximalen Füllstand immer noch etwas
Reserve hast im freien RAM, wird das ganz hervorragend funktionieren.

Ich sehe keinen wirklichen Grund, warum man ein mittlerweile recht
gut debuggtes malloc() an dieser Stelle durch einen eigenen, am
Ende auch bloß nicht effektiveren Ringpuffer oder dergleichen
ersetzen müsste.  Selbstverständlich: es liegt in der Natur der
Sache, dass dir der Speicher ausgehen kann und du keine Telegramme
mehr annehmen kannst.  Aber mit dieser Situation musst du angesichts
der Tatsache, dass die Summe aller Zuflüsse vorübergehend höher sein
kann als die Summe der Verarbeitungsleistung/Abflüsse, ohnehin
umgehen können, egal ob du nun allen RAM statisch belegst, dir
irgendeine eigene Speicherverwaltung schreibst (die sehr wahrscheinlich
erstmal neue Bugs hat ;) oder gleich malloc() nimmst.

realloc() arbeitet auch so, wie du es erwarten würdest: es erweitert
den Speicherbereich in-place, sofern möglich, ansonsten belegt es
einen neuen und kopiert um.  Außerdem defragmentiert free() natürlich
ordnungsgemäß alles, was sich defragmentieren lässt (sofern es
vernünftig implementiert ist).

Nein, die Menge an freiem Heap kann man nicht ohne weiteres ermitteln.
Aber wie Karl-Heinz dir schon schrieb, würde dir eine solche Zahl
nichts nützen, da davon allein nicht klar ist, welche freien Blöcke
welcher Größe gerade existieren.

Benutzt du einen AVR oder was anderes?  In der avr-libc könntest du
über:
1
extern char *__brkval;          /* first location not yet allocated */

den “break value” abfragen, also die erste freie Adresse, die noch
nicht durch den Heap alloziert worden ist.  Wenn du diese mit dem Wert
des Stackpointers vergleichst, hast du ein ungefähres Maß für den
Speicher, der oberhalb des Heap derzeit komplett ungenutzt ist.  (Der
Zeiger wird in der avr-libc mittlerweile auch wieder dekrementiert,
wenn free() einen zusammenhängenden Bereich am oberen Ende des Heaps
freigibt.)  Davon nicht erfasst ist natürlich die sogenannte
freelist, eine verkettete Liste aller „Löcher“, die zwar
mittlerweile wieder freigegeben worden sind, aber irgendwo in der
Mitte des Heaps liegen.

Disclaimer: Es handelt sich hierbei selbstverständlich um meine völlig
voreingenommene eigene Meinung.  Die Voreingenommenheit resultiert
einfach daraus, dass die malloc()-Implementierung der avr-libc zu
großen Teilen auf meinem Mist gewachsen ist. ;-)  Das war damals
gewissermaßen mein „Einstand“ in die Opensource-Welt für den AVR.

von Horst (Gast)


Lesenswert?

Yalu X. schrieb:
> Mir ist gerade noch eingefallen, dass du in meinem Vorschlag von oben
> die Endezeichen nach den Telegrammen auch weglassen kannst, da die
> jeweilige Länge ja bereits in Telegrammdaten enthalten ist. Damit hast
> du praktisch keine Speicheroverhead im Puffer.

Ja das ist mir auch schon aufgefallen :)

Nachteil bei dieser Methode ist allerdings das ich den Telegrammaufbau 
2x Erkennen muss... 1x in der ISR um Zeitgerecht das ACK zu senden bei 
richtiger Adressierung und dann 1x in der Auswertefunktion....

von Horst (Gast)


Lesenswert?

Danke Jörg für deine voreingenommene Meinung :) in meiner Unwissenheit 
über die Details und dem Willen neues zu lernen bin ich eben auf diese 
Fährte aufmerksam geworden.... und auch weil in Objektorientierten 
Sprachen wie zb C++ über new xy eigentlich auch nichts anderes gemacht 
wird als neuer Speicher allociert der vorher nicht statisch reserviert 
wurde und da hinterfragt ja auch keiner oder lieg ich da jetzt falsch?

Den ersten Stack werde ich wohl auf einem CortexM0 (LPC1227) realisieren 
aber wie oben schon erwähnt soll er möglichst flexibel und effektiv sein 
um auch auf kleine 8bittern ein maximum rauszuholen.....

Das mit dem freien Heap hab ich verstanden allerdings melded realloc ja 
wenn er keinen Platz für das nächste Telegramm finden würde, damit geht 
man im Extremfall aber bis an die Genzen des Ram.... wie hällt man sich 
hier die von dir geforderte Reserve im Ram Frei?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg Wunsch schrieb:
> Ich sehe keinen wirklichen Grund, warum man ein mittlerweile recht
> gut debuggtes malloc() an dieser Stelle durch einen eigenen, am
> Ende auch bloß nicht effektiveren Ringpuffer oder dergleichen
> ersetzen müsste.

Nichts läge mir ferner, als dein malloc in irgendeiner Form schlecht
zu reden. Aber in diesem Fall braucht man doch ohnehin eine FIFO-Daten-
struktur (implementiert als Ringpuffer auf einem Array, als verkettete
Liste oder wie auch immer) für einzelne Zeichen oder für ganze Telegram-
me, so dass Verwendung von malloc hier keinerlei Vereinfachung bringt.

von Troll (Gast)


Lesenswert?

DU speicherst aber schon nur die Nutzdaten im Ringpuffer?!

von Hmm (Gast)


Lesenswert?

>Hmm schrieb:
>> Wenn aber im Worst-Case soviele Telegramme auftreten können, ist der
>> Ringbuffer immer noch kleiner als malloc/free und das nicht genutzte RAM
>> kann er ja sowieso nicht anderweitig benutzen,

Yalu schrieb:
>Doch, bei richtiger Speicherorganisation kann der Puffer bei kleinen
>Telegrammen eine größere Anzahl davon aufnehmen, was die Häufigkeit der
>Pufferüberläufe und der dadurch ausgelösten Sendewiederholungen
>reduziert.

Merkwürdig. Ich sehe da den Widerspruch nicht. Vielleicht kann ich ja 
was lernen. Warum schreibst Du "Doch"? Worauf bezieht es sich genau?

1. Ein Ringpuffer braucht bei gleicher Nutzdatenmenge weniger 
Verwaltungsinformation als malloc/free. Sind wir uns da noch einig?

2. Das "nicht genutzte RAM", dass ich meinte, tritt dadurch auf, das zu 
manchen Zeiten weniger als maximal mögliche Telegramme aufkommen bzw. 
eher viele kurze als viele lange. Wenn aber der Worst-Case auch noch 
gehen soll, dann bleibt dadurch eben manchmal RAM frei. Das kann aber 
nicht anders genutzt werden, da es eben im Worst-Case doch genutzt 
werden wird. Der Ringbuffer ist da schon die optimale Organisation, oder 
meinst Du nicht? Wenn wir uns bei Punkt 1 einig sind, dann folgt doch 
daraus, das die Ringbufferlösung mehr Telegramme aufnehmen kann also 
malloc/free.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Horst schrieb:
> Das mit dem freien Heap hab ich verstanden allerdings melded realloc ja
> wenn er keinen Platz für das nächste Telegramm finden würde, damit geht
> man im Extremfall aber bis an die Genzen des Ram.... wie hällt man sich
> hier die von dir geforderte Reserve im Ram Frei?

Nein, die hält man sich nicht frei, sondern die muss insgesamt im
Mittel frei bleiben, damit die Stochastik funktioniert.  Das ist
wie beim Ethernet: bis zu 95 % Auslastung hat man trotz CSMA/CD
gute Datenraten.  Wenn man die letzten 5 % der theoretisch möglichen
Datenrate jedoch ausnutzen will, bricht alles ein, dann geht die
Rate in den Keller.

Keine Ahnung, was für eine Bibliothek du bei deinem LPC benutzt.  Wenn
es die newlib ist (die üblicherweise mit dem GCC genutzt wird), dann
lieferst du dort deine eigene Implementierung für _sbrk(), und damit
solltest du zumindest den höchsten belegten RAM auch wieder wissen.

Yalu X. schrieb:
> Aber in diesem Fall braucht man doch ohnehin eine FIFO-Daten-
> struktur (implementiert als Ringpuffer auf einem Array, als verkettete
> Liste oder wie auch immer) für einzelne Zeichen oder für ganze Telegram-
> me, so dass Verwendung von malloc hier keinerlei Vereinfachung bringt.

Vereinfachung nicht, aber man muss halt nichts statisch vorbelegen
und kommt daher dynamisch an die Grenzen des verfügbaren RAMs heran,
da die üblichen Implementierungen (auch bei ARM) bei jeder neuen
Anforderung nachschauen, ob sie jetzt eine Stack-Heap-Kollision
riskieren oder nicht.

Fertig implementierte Algorithmen für Queues oder Listen auf Basis
von malloc() sollte man ja ausreichend finden, mit denen man den
FIFO implementieren kann.

von Hmm (Gast)


Lesenswert?

@ Yalu:

Seltsam: Du hast das doch hier 
Beitrag "Re: C Speicherverwaltung" selbst auch 
geschrieben.

von Yalu X. (yalu) (Moderator)


Lesenswert?

@Hmm:

Ich dachte, das hier würde sich auf die Ursprungslösung von Horst
beziehen, bei der mehrere 270 Bytes große Puffer in einem Ring
organisiert sind:

Hmm schrieb:
>>Das wäre ja genau die Variante die Speichertechnisch nicht gerade
>>effektiv ist!
>
> Mich würde mal interessieren warum Horst dieser Meinung ist.

Ist ein Telegramm kürzer als 270 Bytes, kann der ungenutzte Teil des
entsprechenden Puffers nicht für andere Telegramme genutzt werden.

Ich sehe aber jetzt gerade, dass sich Horsts und dein Beitrag auf das
hier beziehen:

thomas s. schrieb:
> einen circularbuffer nehmen mit 270byte größe und die telegramme
> herausnehmen und verarbeiten

Diesen Vorschlag habe ich aber nicht verstanden, da ein einzelner Puffer
mit nur 270 Bytes Größe gerade Platz für ein einzelnes "Full-Size"-Tele-
gramm bietet, Horst aber möglichst viele Telegramme puffern will.

von Horst (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Fertig implementierte Algorithmen für Queues oder Listen auf Basis
> von malloc() sollte man ja ausreichend finden, mit denen man den
> FIFO implementieren kann.

Kannst du mir hier noch auf die Sprünge helfen, wo findet man sowas?


@Hmm
Ich glaube ihr redet aneinander vorbei, die Variante mit EINEM 
Ringbuffer mit maximaler Größe der alle Telegramme Byteweise und nicht 
aufgesplittet enthällt ist am Speichereffektivsten, die Variante mit 
einem Ringbuffer der die aufgesplitteten Telegrammbuffer mit der 
statischen Größe von 270Byte enthält ist nicht sehr effektiv!

von Horst (Gast)


Lesenswert?

Yalu X. schrieb:
> thomas s. schrieb:
>> einen circularbuffer nehmen mit 270byte größe und die telegramme
>> herausnehmen und verarbeiten
>
> Diesen Vorschlag habe ich aber nicht verstanden, da ein einzelner Puffer
> mit nur 270 Bytes Größe gerade Platz für ein einzelnes "Full-Size"-Tele-
> gramm bietet, Horst aber möglichst viele Telegramme puffern will.

Ja genau dadurch hab ich das auch erst "Falsch" verstanden.....

von W.S. (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Ich sehe keinen wirklichen Grund, warum man ein mittlerweile recht
> gut debuggtes malloc() an dieser Stelle durch einen eigenen, am
> Ende auch bloß nicht effektiveren Ringpuffer oder dergleichen
> ersetzen müsste.

Jörg, du schreibst mal wieder Unfug. Denk doch mal nach!

Diese ominösen Telegramme bestehen aus Zeichen, die per UART 
nacheinander hereingerauscht kommen, wahrscheinlich per Interrupt oder 
DMA. Sowas fängt man am besten erst mal mit einem Ringpuffer auf, aus 
dem man asynchron zum Empfang ausliest und die Zeichen solange in den 
Bearbeitungspuffer stopft, bis so ein ominöses Telegramm komplett ist 
und bearbeitet werden kann. Für die dafür benötigte Zeit braucht man den 
Ringpuffer unbedingt und er muß so groß sein wie die benötigte 
Verarbeitungszeit relativ zur Baudrate.

Was willst du an dieser Stelle bloß mit irgendwelchen alloziierten 
Blöcken, die wann auch immer wieder freigegeben werden müssen. Ein 
Ringpuffer ist die allereffizienteste Methode, um Daten 
zwischenzuspeichern. Glaub's mir einfach.

Und was soll der TO zwischen 2 "Telegrammen" mit dem RAM anfangen? Mit 
irgendetwas belegen und damit blockieren? Wir sind doch auf nem uC und 
haben kein Swapfile.


W.S.

von Wusel D. (stefanfrings_de)


Lesenswert?

Der Ringpuffer ist niemals fragmentiert. Ungünstig verwendete 
malloc/free Aufrufe führen jedoch zu fragmentiertem Speicher. Und dann 
bist Du aufgeschmissen, dann hilft nur noch ein Reset.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

W.S. schrieb:
> Jörg, du schreibst mal wieder Unfug.

Auf diesem Niveau brauchen wir nicht weiterzudiskutieren.

von Wusel D. (stefanfrings_de)


Lesenswert?

W.S. hat aber Recht.

Möchtest Du fachlich beraten werden, oder über Höflichkeit diskutieren? 
In diesem Forum sollte es um fachliche Themen gehen.

Dass hier oft unhöflich formuliert wird, will ich ja gar nicht 
abstreiten. Aber mal ehrlich: Wer mit anderen Menschen richtig gut 
kommunizieren kann hat andere Hobbies als wir, nicht wahr?

von Falk B. (falk)


Lesenswert?

@ Stefan Frings (stefanfrings_de)

>W.S. hat aber Recht.

Der Ton macht die Musik.

>Möchtest Du fachlich beraten werden, oder über Höflichkeit diskutieren?
>In diesem Forum sollte es um fachliche Themen gehen.

Das bedingt aber ein Minimum an Höflichkeit. Siehe Netiquette.

von Nachdenklicher (Gast)


Lesenswert?

Stefan Frings (stefanfrings_de) schrieb:

> Dass hier oft unhöflich formuliert wird, will ich ja gar nicht
> abstreiten. Aber mal ehrlich: Wer mit anderen Menschen richtig gut
> kommunizieren kann hat andere Hobbies als wir, nicht wahr?

Nein, ist eben nicht wahr. Sowas wie "mal wieder Unsinn" schreiben 
anderen vorzuhalten ist ziemlich respektlos, weil es suggeriert, das 
Gegenüber hätte des öfteren keine Ahnung von dem was er von sich gibt. 
Ich glaube nicht, dass das auf Jörg zutrifft. Nur unfehlbar ist halt 
keiner.

Man hätte auch zum Bleistift schreiben können
- hier irrst du dich
- ich glaube hier liegst du falsch
- das sehe ich ganz anders
usw.

In jeder ernst gemeinten Diskussion fällt man nicht über sich her, denn 
man sieht sich auch im virtuellen Leben mehr als einmal.

von Fuuuuu (Gast)


Lesenswert?

Stefan Frings schrieb:
> W.S. hat aber Recht.
>
> Möchtest Du fachlich beraten werden, oder über Höflichkeit diskutieren?
> In diesem Forum sollte es um fachliche Themen gehen.
>
> Dass hier oft unhöflich formuliert wird, will ich ja gar nicht
> abstreiten. Aber mal ehrlich: Wer mit anderen Menschen richtig gut
> kommunizieren kann hat andere Hobbies als wir, nicht wahr?

WTF! Ganz Unrecht hast du nicht...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Frings schrieb:
> W.S. hat aber Recht.

Ganz unabhängig vom Ton: keiner hat vorgeschlagen, dass man einen
per malloc allozierten Puffer in der ISR selbst benutzt.

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
Noch kein Account? Hier anmelden.