Forum: Compiler & IDEs Wie implementiert man eine Speicherverwaltung?


von TheMason (Gast)


Lesenswert?

Hallo leuts,

ich nochmal. Und wieder mit einer abstrakten (und theoretischen) Frage.
Welche ansätze gibt es eine Speicherverwaltung zu implementieren ?
Ich weiß das das ein Thema ist das in die Betriebssystemprogrammierung 
gehört und auch einiges an Mathematik erfordert (wenn man denn alle nur 
erdenklichen fälle optimalst abdecken möchte), aber kann doch 
prinzipiell nicht so schwierig sein, zumal ja fast jeder c-compiler ein 
malloc unterstützt.
nun ist mir schon klar das man erstmal den zu verwaltenden 
speicherbereich definieren muß. in c würde/könnte man ja einfach ein 
byte-array über (fast) den gesamten speicherbereich definieren, und dann 
ein malloc nachbilden.
aber wie sieht so ein malloc aus ? ich kann mir zwar immer ein stückchen 
vom speicher wegnehmen und auch wieder freigeben (verkettete liste), 
aber wie löst man am einfachsten das problem der fragmentierung (sprich 
: man hat effektiv noch 1024 bytes frei, aber eben nicht 
zusammenhängend). ich weiß das es da 1001 ansätze gibt, aber wie löst 
man sowas am einfachsten ? einfach den speicherbereich umkopieren, und 
dann beim zugriff auf den speicher (mittels dedizierter funktionen) 
dafür sorgen das der nicht in ein loch bzw. auf den falschen speicher 
zugreift ?
wenn ich das problem der fragmentierung mit einem malloc auf einem 
mikrocontroller habe, wie wird das da denn gelöst (wenn es denn gelöst 
wird, oder sagt mir malloc dann einfach : nö, kein speicher mehr frei)

gruß
rene

ps. will ja nicht nerven, bin nur neugierig :-) und möchte etwas mehr 
basiswissen für ein paar zukünftige projekte haben.

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


Lesenswert?

Guck dir doch einfach ein existierendes malloc() an.  Die avr-libc hat
eins, und es ist einigermaßen dokumentiert.

Im K&R steht auch eine einfache Variante drin.

In den Opensource-Unixen kannst du dir dann die Hohe Schule derartiger
Systeme angucken...

von TheMason (Gast)


Lesenswert?

@jörg

erstmal danke für den schnellen link. bin schon am lesen :-)

>In den Opensource-Unixen kannst du dir dann die Hohe Schule derartiger
>Systeme angucken...

besser nicht, sonst könnte ich wahrscheinlich glatt nochmal informatik 
studieren, bevor ich das kapiere :-)), mal ganz abgesehen davon das ich 
es wahrscheinlich eh nicht verstehen werde.

von Rolf Magnus (Gast)


Lesenswert?

> aber wie löst man am einfachsten das problem der fragmentierung (sprich
> : man hat effektiv noch 1024 bytes frei, aber eben nicht
> zusammenhängend).

Kann man nicht.

> einfach den speicherbereich umkopieren, und dann beim zugriff auf den
> speicher (mittels dedizierter funktionen) dafür sorgen das der nicht in
> ein loch bzw. auf den falschen speicher zugreift ?

Das ginge natürlich, aber dann kann man eben wirklich nur noch über 
diese Funktionen auf den Speicher zugreifen. Einfach wie bei malloc 
einen Zeiger dereferenzieren geht dann nicht mehr, weil jeder Zeiger auf 
den Speicherblock beim Herumschieben ungültig wird. Hier wäre dann ein 
"Garbage Collector" im Vorteil. Der kennt alle Zeiger (bzw. kann sie 
ermitteln) und kann sie daher anpassen.

> wenn ich das problem der fragmentierung mit einem malloc auf einem
> mikrocontroller habe, wie wird das da denn gelöst (wenn es denn gelöst
> wird, oder sagt mir malloc dann einfach : nö, kein speicher mehr frei)

Letzteres.

von Bobby (Gast)


Lesenswert?

> Letzteres.

Das ist (für mich) der Grund, auf den malloc-Kram
komplett zu verzichten, und zwar nicht nur bei
Mikrocontrollern...

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Bobby wrote:
>> Letzteres.
>
> Das ist (für mich) der Grund, auf den malloc-Kram
> komplett zu verzichten, und zwar nicht nur bei
> Mikrocontrollern...

Super. Und wie lädst du im PC dann z.B. eine Grafikdatei mit zur 
Compilezeit unbekannten Größe?

uint8_t bigArray[MAYBEMAXFILESIZE];

Matthias

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


Lesenswert?

> Hier wäre dann ein
> "Garbage Collector" im Vorteil.

Das braucht aber ein "handle based"-System.  Du bekommst also keinen
reinen Zeiger, sondern ein "handle", und musst zum Dereferenzieren
dir über dieses Handle einen Zeiger zurückgeben lassen.  Damit kann
die Speicherverwaltung beim Verstopfung im Hintergrund den Speicher
umorganisieren.

Gibt's dafür eigentlich einen de-facto-API-Standard?  Klingt ja
eigentlich wie eine nette Ergänzung für die avr-libc.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch wrote:
>> Hier wäre dann ein
>> "Garbage Collector" im Vorteil.
>
> Das braucht aber ein "handle based"-System.  Du bekommst also keinen
> reinen Zeiger, sondern ein "handle", und musst zum Dereferenzieren
> dir über dieses Handle einen Zeiger zurückgeben lassen.  Damit kann
> die Speicherverwaltung beim Verstopfung im Hintergrund den Speicher
> umorganisieren.
>
> Gibt's dafür eigentlich einen de-facto-API-Standard?

Nicht das ich wüsste.

>  Klingt ja
> eigentlich wie eine nette Ergänzung für die avr-libc.

Ist so wie die Speicherverwaltung in Windows ganz am Anfang
funktioniert hat. Ist ein 'pain in the ass'.
Du brauchst nämlich auch noch Lock und Unlock Funktionen, damit
du der Speicherverwaltung klar machen kannst, dass es einen
bestimmten Speicherbereich jetzt gerade nicht verschieben darf,
weil du zugreifen willst.

von Bobby (Gast)


Lesenswert?

> Super. Und wie lädst du im PC dann z.B. eine Grafikdatei mit zur
> Compilezeit unbekannten Größe?

Garnicht !
Wer sowas braucht darf natürlich gerne mallocen solange er will.

BTW: Ich erstelle Visualisierungen für industrielle Anwendungen.
Diese sollen wenn möglich nicht nur ein paar Wochen am Stück laufen.

von Rolf Magnus (Gast)


Lesenswert?

Das braucht er aber auch nur, wenn er nebenher in einem eigenen Thread 
läuft. Das ist eh ungünstig, weil das Verhalten dann nicht 
deterministisch ist. Der Collector räumt halt auf, wenn er grad Lust 
dazu hat. Man könnte aber eine Garbage Collection auch so machen, daß 
sie explizit angestoßen wird. Dann kann man einerseits aufräumen lassen, 
wenn man gerade Zeit hat (oder auch erst, wenn man mal neuen Speicher 
braucht, für den kein Platz mehr ist), andererseits geht das ganze auch 
synchron, und man braucht kein Locking.

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


Lesenswert?

Bobby wrote:

> BTW: Ich erstelle Visualisierungen für industrielle Anwendungen.
> Diese sollen wenn möglich nicht nur ein paar Wochen am Stück laufen.

Ja, und?

Andere Leute schreiben ganze Betriebssysteme, die Jahre am Stück
laufen, und sie können trotzdem malloc() benutzen.

Das Fragmentierungsproblem existiert vor allem auf kleinen Maschinen,
auf VM-Architekturen ist das bei vernünftiger Programmierung kein
ernsthaftes Problem.  Bei einer langleben Applikation mit viel
malloc()/free() wird es sich statistisch ohnehin auf einem bestimmten
Niveau einpegeln, statt ohne jede Grenze nach oben zu wachsen.

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus wrote:
> Das braucht er aber auch nur, wenn er nebenher in einem eigenen Thread
> läuft. Das ist eh ungünstig, weil das Verhalten dann nicht
> deterministisch ist. Der Collector räumt halt auf, wenn er grad Lust
> dazu hat. Man könnte aber eine Garbage Collection auch so machen, daß
> sie explizit angestoßen wird. Dann kann man einerseits aufräumen lassen,
> wenn man gerade Zeit hat (oder auch erst, wenn man mal neuen Speicher
> braucht, für den kein Platz mehr ist), andererseits geht das ganze auch
> synchron, und man braucht kein Locking.

Das Problem ist, dass du die Garbage Collection dann maximal
im main() aufrufen darfst, wenn sonst nichts anderes im
System läuft. Und dann darfst du natürlich nicht vergessen, dass
alle Pointer die du bereits hast ungültig sind und neu aus
den Handles generiert werden muessen.

> oder auch erst, wenn man mal neuen Speicher
> braucht,

Genau dieser Fall artet dann in ein Nightmare aus.
Stell dir vor du hast eine Funktion, in der mehrere
mallocs involviert sind. Nach jedem malloc kann u.U
der GC aufgerufen werden. Das heist aber auch, dass
alle bisherigen Pointer in der Funktion ungültig sind
und neu generiert werden muessen. Das gilt auch für den
Aufrufer dieser Funktion:

void foo()
{
  char* pPtr = malloc( 10 );

  if( pPtr == NULL ) {
    GC();
    pPtr = malloc( 10 );   // ich spar mir hier die Fehlerbehandlung
  }

  *pPtr = 5;        // mach was damit

  // soweit so gut. Jetzt ruft die Funktion eine andere Funktion auf
  foo2();

  // an dieser Stelle: ist pPtr noch gültig? Oder hat ein Aufruf
  // von GC() in foo2() den Pointer ungütlig gemacht

  *pPtr = 8;
}

void foo2()
{
  char* pPtr1 = malloc(20);

  if( pPtr1 == NULL ) {
    GC();
    pPtr1 = malloc( 20 );   // wieder: Fehlerbehandlung gespart
  }

  char* pPtr2 = malloc( 100 );

  if( pPtr2 == NULL ) {
    GC();                  // pPtr1 ist damit ungültig geworden
    pPtr1 = malloc( 20 );  // der kann aber immer noch schief gehen
    pPtr2 = malloc( 100 );
  }

}


Die entsprechenden free() hab ich mir jetzt gespart.
Aber man sieht auch so schon: Das artet in Chaos aus.
Im Grunde weist du nach einem Funktionsaufruf nie, ob alle
deine Pointer überhaupt noch gültig sind.
Im Grunde hast du bei mehreren Allokierungen in einer Funktion
enorme Allokierungs-Probleme.


In C++ könnte man dafür eine schöne Klasse schreiben, die einen
Handle kapselt und bei jeder Dereferenzierung den Handle in
einen Pointer umwandelt. Aus Gründen der Laufzeit: Will man das?

von Arc N. (arc)


Lesenswert?

Wenn malloc anders arbeiten würde, ginge es:

bool malloc(void** ptr, size_t size);

Nur löst das nicht die Probleme, die auftreten, wenn der Pointer 
irgendwo anders verwendet werden soll (dann müsste man wieder mit einem 
Handle arbeiten und sich daran halten...).
Das Problem der Fragmentierung kann man z.B. abmildern, indem man 
"größere" Blöcke direkt belegt und "kleinere" Blöcke separat (z.B. 
innerhalb eines größeren) verwaltet.

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.