Suchen

Echtzeitbetriebssysteme – Einführung und Konzepte

Seite: 3/4

Firmen zum Thema

Dynamische Speicher-Allokation in Echtzeitbetriebssystemen

Bild 7: Speicherpool – Liste der verfügbaren Puffer
Bild 7: Speicherpool – Liste der verfügbaren Puffer
(Bild: Kalinsky Associates)

Auch im Bereich der dynamischen RAM-Allokation spielt das deterministische Zeitverhalten eine wichtige Rolle. Bei vielen normalen Betriebssystemen wird Speicher aus einem Heap zugewiesen. Die Services „malloc“ und „free“, bekannt aus der C-Programmierung, arbeiten mit einem Heap. Über den Aufruf von „malloc“ kann eine Task Speicherbereiche aus dem Betriebssystem-Heap temporär nutzen und die erforderliche Speichergröße definieren.

Wenn diese (oder eine andere) Task den Inhalt des entsprechenden Speichers abgearbeitet hat, kann sie den Speicher über den Aufruf von „free“ an das Betriebssystem zurückgeben. Das Betriebssystem gibt den Speicher an den Heap zurück, wo er z.B. als Teil eines großen Puffers wiederverwendet oder später in mehrere kleinere Puffer aufgeteilt werden kann.

Die bei Heaps auftretende „externe Speicherfragmentierung“ kann die Leistung der Heap-Services beeinträchtigen: Ein Puffer, der dem Heap zurückgegeben wurde, wird möglicherweise in kleinere Puffer aufgebrochen, sobald über einen „malloc“-Aufruf kleinere Puffergrößen anfordert werden. Ein Heap, der bereits mehrere malloc- und free-Zyklen durchlaufen hat, weist häufig kleinere Speicherabschnitte zwischen den Pufferspeicherbereichen auf, die von den Tasks verwendet werden.

Diese Abschnitte sind so klein, dass sie für die Tasks nicht nutzbar sind. Jedoch stecken sie zwischen den Puffern fest, die von den Tasks verwendet werden, und lassen sich deshalb nicht in zusammenhängende Puffer mit nutzbarer Größe zusammenfügen. Mit der Zeit hat ein Heap immer mehr dieser kleinen, nicht nutzbaren Speicherabschnitte. Dies kann dazu führen, dass Anfragen einer Task (malloc) nach Pufferspeicher mit einer bestimmten Größe vom Betriebssystem nicht bedient werden, obwohl im Heap genügend Speicher zur Verfügung steht. Dieser Speicher ist jedoch in sehr kleine Abschnitte zerteilt, die sich an verschiedensten Stellen im Heap befinden. Im Kontext von Betriebssystemen bezeichnet man diese Abschnitte als „Fragmente“ und das damit verbundene Problem als „externe Speicherfragmentierung“.

Dieses Problem der Fragmentierung lässt sich durch eine sogenannte „Garbage Collection“ Software zur automatischen Speicherbereinigung beheben (Defragmentierung). Leider sind Garbage Collection Algorithmen oft alles andere als deterministisch und verursachen zufällig auftretende Delays mit zufälliger Dauer in den Heap-Services. Dazu kommt es häufig in den Speicherallokations-Services normaler Betriebssysteme.

Embedded-Software-Entwickler, die ein normales Betriebssystem verwenden wollen, stecken also in der Klemme. Dürfen in dem Embedded-System gelegentlich zufällige Delays von zufällig langer Dauer auftreten, wenn die Garbage Collection ausgeführt wird? Oder erlaubt man dem Embedded-System, seinen Speicher zu fragmentieren, bis „malloc“-Anforderungen der Applikationssoftware an den Heap zurückgewiesen werden, obwohl noch genügend freier Speicher zur Verfügung steht? Für Embedded-Systeme, die ihre Services über lange Zeitspannen zuverlässig erbringen müssen, ist keiner der beiden Ansätze akzeptabel.

Echtzeitbetriebssysteme dagegen umgehen dieses Dilemma, indem sie sowohl die Speicherfragmentierung und Garbage Collection als auch die damit verbundenen Folgen vermeiden. Sie verwenden eine nicht-fragmentierende Speicherallokation anstelle von Heaps und stellen der Applikationssoftware nur bestimmte Speichergrößen zur Verfügung. Dieser Ansatz ist zwar nicht so flexibel wie der Einsatz von Speicher-Heaps. Die externe Speicherfragmentierung wird so jedoch vermieden, und es ist keine Defragmentierung erforderlich.

Der „Pools“-Mechanismus zur Speicherallokation beispielsweise ermöglicht es, Speicherbereiche mit 4 oder 8 verschiedenen Größen je Pool zuzuweisen. Pools vermeiden die externe Speicherfragmentierung und gestatten nicht, dass ein an den Pool zurückgegebener Puffer später in kleinere Teile gesplittet wird. Der Puffer kommt stattdessen bei Rückgabe auf eine Liste freier Puffer derselben Größe (free buffer list).

Diese Puffer stehen dann in ihrer ursprünglichen Größe zur Wiederverwendung zur Verfügung, siehe Bild 7. Die Speicherallokation und -deallokation erfolgt mit deterministischem und oft konstantem Timing.

(ID:44941011)