Forum: Mikrocontroller und Digitale Elektronik Calloc() bei eingebetteten Systemen


von Mike (Gast)


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

von Sven B. (scummos)


Lesenswert?

Die zwei einfachen Gründe sind dass man einen Algorithmus braucht, der 
den Speicher vergibt (was nicht ganz trivial ist) und dass man bei CPUs 
ohne MMU tatsächlich Probleme mit der Fragmentierung bekommt.

von Dirk B. (dirkb2)


Lesenswert?

Was weißt du denn über die Funktionsweise von malloc?
Wie oft willst du es einsetzen?

von Einer K. (Gast)


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.

von Stefan F. (Gast)


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.

von Mike (Gast)


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?

von Stefan F. (Gast)


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.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Mike schrieb:
> Gibts hier Gründe
> für dynamische Speicherverwaltung?

Ich wuesste keine Gruende fuer gewaltsamen calloc() Einsatz, nichtmal 
bei "ausgebetteten" Systemen, wenn von vorneherein feststeht, wie gross 
der Platzbedarf an Speicher wird.

Gruss
WK

von Mark B. (markbrandis)


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.

von Mike (Gast)


Lesenswert?

Danke für eure Antworten!

von Nop (Gast)


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.

von Rolf M. (rmagnus)


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.

von Nop (Gast)


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.

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