Wir kommen zur nächsten Übung in der Systemprogrammierung. Diesmal wollen wir uns anschauen, wie der Adressraum
eines Prozesses aussieht. Dazu haben wir folgendes kleines Codebeispiel. Im Folgenden wollen
wir uns den Adressraum aufzeichnen, der entsteht, wenn wir dieses Beispiel kompilieren und ausführen.
Das Beispiel besteht aus einigen globalen Variablen, nämlich den Static Integern a-c
und einem konstanten Integer f. Außerdem haben wir ein String helloWorld, auf dem der Zeiger
s zeigt. Darüber hinaus gibt es eine Funktion main, die wiederum zwei lokale Variablen hat.
Einmal die Integer Variable g und eine Static Integer Variable h. Ein Adressraum sieht ungefähr
aus wie hier abgebildet. Wir haben an den hohen Adressen das Stack-Segment, in dem die
lokalen Variablen liegen. An den niedrigen Adressen haben wir das Code-Segment, auch
genannt Text-Segment, in dem die ausführbaren Instruktionen des Programms liegen. Außerdem
liegen hier auch die Konstanten, wie zum Beispiel der Wert 42, der Variable f, oder eben der
String helloWorld. An den Adressen darüber haben wir einmal das Punkt Data-Segment und
dann das Punkt BSS-Segment. Im Punkt Data und Punkt BSS liegen ebenfalls globale Variablen.
Diese sind jedoch les- und schreibbar. Der Unterschied zwischen Punkt Data und Punkt
BSS ist, dass im Punkt BSS alle nicht initialisierten Daten liegen. Das bedeutet alle Variablen,
denen im Quellcode kein Wert zugewiesen wurde. Diese werden automatisch mit dem Wert Null
initialisiert beim Laden des Programms. In dem Beispiel betrifft das zum einen die Variable
b, der kein Wert zugewiesen wird. Abhängig vom Compiler kann es auch sein, dass Variablen,
die explizit mit dem Wert Null initialisiert werden, ebenfalls in das Punkt BSS-Segment
verortet werden. Alle initialisierten Daten, wie zum Beispiel die Variable a und der Zeiger
s, liegen im Punkt Data-Segment. Hier nochmal eine kurze Erinnerung, was das Schlüsselwort
Static bedeutet. Im Kontext von globalen Variablen bedeutet Static, dass der Name der Variable
nur innerhalb des Moduls sichtbar ist. Das bedeutet andere C-Module können über den
Namen nicht auf die Variable zugreifen. Wenn Static jedoch an lokale Variablen innerhalb
von Funktionen geschrieben wird, hat es eine etwas andere Bedeutung. In dem Fall bedeutet
es, dass der Wert der Variable über Funktionsaufrufe hinweg erhalten bleibt. Damit dies umgesetzt
werden kann, darf die Variable nicht auf dem Stack stehen, da dieser nach dem Ausführen
des Return Statements aufgeräumt wird und die Werte, die der jeweiligen Funktion auf
dem Stack zugeordnet waren, verloren sind. Als Konsequenz müssen statisch lokale Variablen
also ebenfalls im Daten-Segment liegen. An dieser Stelle fragt man sich vielleicht, warum
man die Unterscheidung zwischen einem Data- und einem BSS-Segment trifft, wo es doch möglich
wäre, die Daten aus dem BSS-Segment einfach explizit mit Null zu initialisieren und ebenfalls
ins Data-Segment zu schreiben. Der Grund dafür liegt in der praktischen Umsetzung. Alle initialisierten
Variablen müssen im ausführbaren Programm gespeichert werden, damit zum Zeitpunkt des
Ladens des Programms die Werte in den Speicher geschrieben werden können. Das bedeutet,
für jede Variable brauche ich einen extra Speicherplatz, der ebenfalls in der Binärdatei
auf der Festplatte allokiert sein muss. Wenn ich nun jedoch eine große Menge von nicht
initialisierten Daten habe, die implizit mit dem Wert Null vorbelegt werden, reicht das,
wenn ich mir merke, wie viel Byte ich mit Null initialisieren möchte und brauche nicht
für jede Variable selbst den Wert Null in der ausführbaren Datei zu speichern.
Damit sinkt der Speicherverbrauch auf der Festplatte. Das könnt ihr auch beobachten,
wenn ihr euch nun noch einmal eure Halde anschaut. In der Halde allokiert ihr über einen Megabyte
statisch in einem Array. Das ausführbare Programm jedoch verbraucht auf der Festplatte
weniger als einen Megabyte Speicher, eben weil in dem Binary der Verweis steht, dass
zum Zeitpunkt des Ladens des Programms 1 Megabyte Nullen in den Speicher geschrieben
werden sollen, anstelle dass tatsächlich 1 Megabyte Nullen in dem Binary stehen.
Was nun auffällt ist, dass zwischen dem Stack-Segment und dem Heap, der aus dem Punkt Daten und
Punkt BSS-Segment besteht, ein großer freier Platz ist. Dies ist bewusst, damit zur Laufzeit
neue Speicher allokiert werden kann. So wächst der Stack zum Beispiel von hohen Adressen
nach unten zu niedrigen, während der Heap von niedrigen Adressen nach hohen wächst.
Zugänglich über
Offener Zugang
Dauer
00:11:46 Min
Aufnahmedatum
2020-06-21
Hochgeladen am
2020-06-21 23:26:34
Sprache
de-DE