Calloc() bei eingebetteten Systemen

Gast #4664237
Lesenswert?

Hallo zusammen,

ich habe nun schon mehrmals gelesen, dass man auf dynamische 
Speicherveraltung bei Steuergeräten eher verzichtet. Begründet wurde 
dies meistens mit der Fragmentierung des Speichers und das man damit 
kein deterministisches Verhalten mehr hat.
Verstehen kann ich das allerdings nicht so ganz. Kann mir das vielleicht 
mal einer näher erläutern oder gibt es generell Gründe dafür oder 
dagegen.

Danke!

Gruß Mike
Gast #4664254
Lesenswert?

Mike schrieb:
> Verstehen kann ich das allerdings nicht so ganz.

Wenn der Speicher fragmentiert ist, sagen wir mal, in 32Byte Blöckchen, 
getrennt durch vergebene Blöckchen, und du versuchst 128Byte zu 
reservieren...
Was passiert dann?
Richtig, er nimmt sichs am oberen Ende.
Und da kommt ihm der Stack entgegen.

Heap <-> Stack Kollisionen drohen.


Nein, das möchte ich in keinem Steuergerät sehen!
Schon gar nicht beim Airbus, der gerade zu den Malediven fliegt.
Und auch nicht bei einem ABS System, während einer Vollbremsung.
Gast #4664271
Lesenswert?

Die Fragmentierung entsteht so:

Stell Dir vor du hast insgesamt 50 Bytes RAM frei und belegst 10 mal 2 
Bytes, und dann 1 mal 10 Bytes:

AABBCCDDEEFFGGHHIIJJKKKKKKKKKK

Und dann gibst du jeden zweiten Block frei:

AA..CC..EE..GG..II..KKKKKKKKKK

Danach hast du einen fragmentierten Speicher. Du hast zwar insgesamt 
noch 10 Bytes frei, kannst aber höchstens 2 Bytes am Stück belegen.

Ob solche Lücken entstehen, und ob diese Lücken zu klein sind, hängt 
natürlich sehr vom jeweiligen Programm ab. Fragmentierung ist daher 
nicht zwangsläufig ein Problem.

Vor vielen Jahren hatte ich dieses Problem auf meinem Windows 98 Rechner 
beobachtet. Größere Programme liessen sich irgendwann nicht mehr 
starten, dann musste man rebooten und es ging wieder. Ich vermute, dass 
Microsoft inzwischen eine clevere Lösung dagegen gefunden hat.

Bei dieser Veranschaulichung habe ich vernachlässigt,
a) dass die Speicherverwaltung selbst auch Speicher benötigt und
b) dass der Heap je nach Systemarchitektur mit dem Stack kollidieren 
kann und
c) Speicher meisst in größeren Blöcken vergeben wird
Genaue Details dazu kannst du in der Doku der jeweiligen C Library 
nachlesen.
Gast #4664280
Lesenswert?

U. F. schrieb:
> Wenn der Speicher fragmentiert ist, sagen wir mal, in 32Byte Blöckchen,
> getrennt durch vergebene Blöckchen, und du versuchst 128Byte zu
> reservieren...
> Was passiert dann?
> Richtig, er nimmt sichs am oberen Ende.
> Und da kommt ihm der Stack entgegen.
>
> Heap <-> Stack Kollisionen drohen.

Das verstehe ich natürlich. Vielleicht muss ich meine Frage etwas anders 
formulieren. Angenommen ich habe ein Programm, dass 10 mal calloc 
aufruft und ich bin mir sicher, dass es keine Kollision zwischen Heap 
und Stack geben kann. Das Programm ruft bei jedem Start exakt 10 mal 
calloc auf und immer in der gleichen Init Funktion. Ich weiss also zur 
Compilezeit, dass genau 10 Instanzen angelegt werden. Gibts hier Gründe 
für dynamische Speicherverwaltung?
Gast #4664297
Lesenswert?

Wenn du schon vor der Laufzeit weisst, wie viel Speicher das Programm 
brauchen wird, dann belege ihn besser statisch. Denn dann kann der 
Compiler das auch prüfen und "unerklärliche" Fehler sind bei künftigen 
Änderungen ausgeschlossen.
#4664321
Lesenswert?

Der Sinn von malloc() und calloc() ist ja gerade, dass man sie verwendet 
wenn man NICHT vorher weiß wieviel Speicher zur Laufzeit benötigt werden 
wird.

Bei einem Webshop weißt Du zum Beispiel vorher nicht, wie viele und 
welche Artikel der Kunde in seinen Warenkorb legen wird.

Bei einem Steuergerät für Automotive, Luft- und Raumfahrt etc. weiß man 
in aller Regel vorher, wieviel Speicher man wofür braucht.
Gast #4664908
Lesenswert?

Wenn man verschiedene speicherintensive Aufgaben hat, die aber nie 
zugleich ausgeführt werden, dann kann man das statt über malloc auch mit 
einem entsprechend groß dimensionierten Stack lösen. Der fragmentiert 
dann nicht.

Muß man nur drauf achten, daß der Compiler die entsprechenden Funktionen 
nicht inlined. Gerade bei GCC sollte man das mit entsprechenden 
Funktionsattributen erzwingen.

Im Übrigen halte ich es ohnehin für Wahnsinn, den Heap auf den Stack 
zuwachsen zu lassen. Oder den Stack auf den Heap, bzw. bei Abwesenheit 
des Heaps auf die globalen Variablen. Den Heap legt man nach ganz oben, 
den Stack nach ganz unten (bei descending stack). Wenn man einen Stack 
Overflow kriegt, dann hat man kein undefiniertes Systemverhalten, das 
womöglich zunächst einmal gar nicht auffällt, sondern einen klaren 
Absturz. Wenigstens weiß man dann, daß da irgendwo ein Bug ist.
#4664951
Lesenswert?

Mike schrieb:
> ich habe nun schon mehrmals gelesen, dass man auf dynamische
> Speicherveraltung bei Steuergeräten eher verzichtet. Begründet wurde
> dies meistens mit der Fragmentierung des Speichers und das man damit
> kein deterministisches Verhalten mehr hat.
> Verstehen kann ich das allerdings nicht so ganz. Kann mir das vielleicht
> mal einer näher erläutern oder gibt es generell Gründe dafür oder
> dagegen.

Die Fragmentierung ist das eine, aber bei einem Steuergerät dürfen in 
der Regel Teile der Funktion nicht einfach temporär mal nicht tun, nur 
weil grad der Speicher voll ist. Es muss für den Worst-Case für alle 
Situationen genug Speicher da sein. Dann kann man aber auch gleich alles 
statisch allokieren und braucht gar keinen dynamischen Speicher.

Nop schrieb:
> Im Übrigen halte ich es ohnehin für Wahnsinn, den Heap auf den Stack
> zuwachsen zu lassen. Den Heap legt man nach ganz oben, den Stack nach
> ganz unten (bei descending stack). Wenn man einen Stack
> Overflow kriegt, dann hat man kein undefiniertes Systemverhalten, das
> womöglich zunächst einmal gar nicht auffällt, sondern einen klaren
> Absturz. Wenigstens weiß man dann, daß da irgendwo ein Bug ist.

Was ist denn bei einem µC ein "klarer Absturz"? Je nach Prozessor gibt's 
evtl. auch einfach einen Wrap-around, und der Stack läuft vom 
Speicherende runter weiter. Oder auf z.B. einem AVR läuft er zuerst in 
die I/O-Register, dann in die ALU-Register rein. Gibt bestimmt auch 
lustige Effekte.
Gast #4665253
Lesenswert?

Rolf M. schrieb:
> Was ist denn bei einem µC ein "klarer Absturz"?

Dasselbe wie bei jedem anderen Prozessor auch: eine fault exception. Auf 
Cortex-M beispielsweise hat man einen hard fault, wenn man unterhalb des 
RAMs reinschreibt.

Dann gehts in den exception handler, um erstens den Fehler zu 
signalisieren und zweitens zu rebooten. Immer noch besser, als mit einem 
undefinierten Zustand weiterzulaufen. Wobei der handler für einen stack 
overflow nicht ganz trivial ist, das geht nur in Assembler.

> Je nach Prozessor gibt's
> evtl. auch einfach einen Wrap-around, und der Stack läuft vom
> Speicherende runter weiter.

Kann natürlich auch sein. Wenn der Prozessor sowas halt nicht 
unterstützt, dann hilft nur noch das Erstellen eines call trees, der die 
benötigte Stackgröße ausgibt. Was zumindest dann geht, wenn man keine 
Rekursion verwendet. Den Stackbedarf der ISRs muß man natürlich auch 
noch draufschlagen.

> Oder auf z.B. einem AVR läuft er zuerst in
> die I/O-Register, dann in die ALU-Register rein.

Ja, das zusammen damit, daß der Stack dann auch noch nach unten wächst, 
finde ich das kein besonders glückliches Hardwaredesign. Allerdings, 
wenn man z.B. nur 512 Bytes an SRAM hat, dann werden das ohnehin nur 
sehr kleine Anwendungen sein, die man dann auch von Hand noch 
analysieren kann.

Ich ziehe halt MCUs vor, bei denen ich da ein klares Fehlerverhalten 
kriegen kann. Zumal der Compiler einem auch noch dazwischenhauen kann, 
wenn der bei Optimierung auf Größe mit GCSE arbeitet und man dann 
abenteuerliche Verschachtelungen kriegen kann.

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