Zeigerwerte in C



  • ach soooo jetzt verstehe ich das erst...

    man umgeht die segmentierung einfach, obwohl sie eigentlich vorhanden ist 🙂

    Danke.

    Wie hat man sich denn dann diese Semgente (Stacksegment, Heap-Segment) im virt.Adressraum eines Prozesses vorzustellen?
    So gesehen sind das dann ja, wie ihr schon gesagt habt gar keine richtigen Segmente.. Wer verwaltet das ganz dann? der Compiler?

    Und wie wird realisiert, dass der Speicherbereich, indem Beispielsweise der Programmcode steht, nicht verändert werden kann? Kann man die Pages dementsprechend markieren?

    gruß 🙂



  • MatheStein schrieb:

    Wie hat man sich denn dann diese Semgente (Stacksegment, Heap-Segment) im virt.Adressraum eines Prozesses vorzustellen?

    also ich stell mir das so vor:

    +------------+ <-+
    |    data    |   | data seg ist static
    +------------+ <-+
    |    heap    |   |
    +------------+ <-+-- heap ist variable
    |            |
    |            |
    |            |
    +------------+ <-+
    |    stack   |   |
    +------------+ <-+-- stack ist variable
    

    stack und heap kommen sich entgegen

    int main(void)
    {
    int x = 11;
    int y = 11;
    int *xx = &x;
    int *yy = &y;
    char bb[] = "asdasdsa";
    char *a0 = &bb[1];
    void *ll= &&LOLO;
    char *asdasdasd = "asdasdsa";
    void *data = malloc(sizeof(int));
    LOLO:
    return 0;
    }
    

    kpl. ob meine vorstellung stimmt 😕
    lg lolo



  • axo ja und die cpu hat natürlich ein paar register stack und base pointer, in dem programm sind dann nur die offsets auf die adressen abgelegt daher kann es auch mit beliebigen adress bereichen umgehen was ja nicht möglich wär, wenn die gesamte adresse fest rein gecoded ist 😕

    also sind im programm scheinbar nur die offsets auf stack und base pointer fest verdrahtet und die pointer kommen über %ebp und %esp welche eigentlich nicht verändert werden?

    lg lolo



  • MatheStein schrieb:

    man umgeht die segmentierung einfach, obwohl sie eigentlich vorhanden ist

    stimmt, gute CPUs mit 2^32 addressraum haben auch kein nerviges segmentierungs-gedöns, das wollte man den x86'ern auch angedeihen lassen.

    MatheStein schrieb:

    Wie hat man sich denn dann diese Semgente (Stacksegment, Heap-Segment) im virt.Adressraum eines Prozesses vorzustellen?

    es sind einfach willkürlich festgelegte bereiche im adressraum (von...bis).

    MatheStein schrieb:

    So gesehen sind das dann ja, wie ihr schon gesagt habt gar keine richtigen Segmente.. Wer verwaltet das ganz dann? der Compiler?

    das betriebssystem verwaltet das. bevor ein prozess gestartet wird, wird für ihn virtueller speicher erzeugt, heap, mindestens ein stack, primärer thread, statisches datensegment, codesegment usw. werden angelegt und dann wird er da 'reingeladen', initialisiert und gestartet. (unter windoof wohlgemerkt, linux macht das anders, mit irgendwelchen fork-faxen *fg)

    MatheStein schrieb:

    Und wie wird realisiert, dass der Speicherbereich, indem Beispielsweise der Programmcode steht, nicht verändert werden kann? Kann man die Pages dementsprechend markieren?

    bei den x86'ern haben speicherseiten attribute, wie read, write, execute und kombinationen daraus. der code wird z.b. in einen 'execute-read'-bereich geladen. damit kann er ausgeführt werden und auf seinen eigenen maschinencode lesend zugreifen. versucht er einen schreibzugriff, gibts 'ne exception, unter windoof gibts dann eine niedliche messagebox 'general protection fault', oder so, und der prozess wird gekillt.
    🙂



  • danke für eure Antworten.

    Mich verwirrt jetzt wieder, was noobLolo geschrieben hat.

    Genau so mit den Segmentregistern und den "abgelegten Offsets im Programm" ist es doch gerade nicht mehr oder? Das war ja eigentlich meine ursprüngliche Frage 🙂

    Gruß



  • MatheStein schrieb:

    Mich verwirrt jetzt wieder, was noobLolo geschrieben hat.

    lass dich durch micht nicht verwirren, versuche das selbst zu verstehen, hab da noch nicht so ganz den durchblick, evtl. muß man das auch einfach mal von grund auf gemacht haben das man das checkt, die theorie ist immer so ne sache bei mir, bin eher der praktiker und wenn ichs mal selbst gemacht hab, sollte ich es auch verstanden haben, habs aber bisher nicht gemacht und aus diesem grund auch noch nicht verstanden 😞

    lg lolo



  • Wenn der Compiler Maschinencode generiert wird durchaus die Base+Offset Methode zur Adressierung genutzt. Vor allem beim Stack wird in der Regel immer relativ zum Base Pointer (Der eine Kopie des Stackpointers zu Beginn der aktuellen Funktion ist) gearbeitet.

    Das ist aber trotzdem keine Segmentierung, und der Stackpointer ist kein Segmentregister. Es gibt einfach verschiedene Arten von mov Befehlen. Die eine nimmt einen Wert als absolute Adressen, die andere ein Register als Basis und den absoluten Wert als Offset. Das läuft sogesehen auf der Befehlsebene. Bei der Segmentierung wird eine Ebene tiefer die Adressierung an sich geändert und betrifft damit so ziemlich alle Befehle die irgendwie auf Adressen zugreifen.

    Technisch ist der Unterschied nicht so groß, der Grund warum man das eine oder das andere einsetzt bzw. eingesetzt hat sind ganz andere.



  • ok 🙂 das war dann soweit ganz verständlich danke 🙂

    Eine Sache vllt noch 🙂

    Wenn ein Prozess erstellt wird, dann bekommt er (wie wir jetzt wissen) einen virtuellen Adressraum der Größte 2^32 Byte vom BS übergeben.

    Soweit so gut..

    Wer sorgt denn jetzt zB dafür, dass die Pages, auf denen zB der Quellcode liegt, nicht beschreibar sind und vor allem wie macht der das?

    Und vllt noch eine etwas allgemeinere Frage:

    Wenn ich einen Prozess erzeuge und gerade meinen virt.Adressraum bekommen habe, dann habe ich somit zugriff auf eine Menge von Pages, die aber alle initial weder im RAM noch auf der Festplatte sind.
    Könnte ich jetzt einfach nach belieben eine Page beschreiben und der "initiale Pagefault" für diese Page sorgt dann dafür, dass eine Kachel der zu beschreibenden Page zugeordnet wird?
    Oder muss initial erst noch dem BS explizit mitgeteilt werden, dass man zB auf die Page mit Adresse 0x12345678 schreiben will und somit die Page mit einer syscall erst zugänglich machen?

    Gruß und nochmals vielen Dank für die vielen Hilfen 🙂



  • MatheStein schrieb:

    Wer sorgt denn jetzt zB dafür, dass die Pages, auf denen zB der Quellcode liegt, nicht beschreibar sind und vor allem wie macht der das?

    die pages haben 'deskriptoren' mit flags für die zugriffsrechte (schreiben, lesen, code ausführen, usw.)

    MatheStein schrieb:

    Könnte ich jetzt einfach nach belieben eine Page beschreiben und der "initiale Pagefault" für diese Page sorgt dann dafür, dass eine Kachel der zu beschreibenden Page zugeordnet wird?

    ja, mit allen konsequenzen. wenn z.b. alles RAM in benutzung ist, dann werden pages eines anderes prozesses auf die platte geschrieben, er wird eingefroren und ihm wird das RAM entzogen, damit dein prozess speicher hat. irgendwann ereilt deinen prozess das gleiche schicksal, denn die anderen wollen ja auch mal wieder dran kommen.
    🙂



  • ;fricky schrieb:

    MatheStein schrieb:

    Wer sorgt denn jetzt zB dafür, dass die Pages, auf denen zB der Quellcode liegt, nicht beschreibar sind und vor allem wie macht der das?

    die pages haben 'deskriptoren' mit flags für die zugriffsrechte (schreiben, lesen, code ausführen, usw.)

    Ok, aber wie setzt man die Flags der Page?
    Wenn der Prozess erstellt wird, dann sind die Flags aller Pages schon gesetzt oder nicht?
    Könnte man diese im nachhinein beliebig ändern, würde das auch nicht viel Sinn ergeben, weil sich das Programm, der dem Prozess zugeordnet ist, so im nachhinein alles beliebig definieren könnte und zB alle Pages seines Adressraum auf Lesen/Schreiben/Ausführen setzen könnte.

    Gruß 🙂



  • MatheStein schrieb:

    Ok, aber wie setzt man die Flags der Page?
    Wenn der Prozess erstellt wird, dann sind die Flags aller Pages schon gesetzt oder nicht?
    Könnte man diese im nachhinein beliebig ändern, würde das auch nicht viel Sinn ergeben, weil sich das Programm, der dem Prozess zugeordnet ist, so im nachhinein alles beliebig definieren könnte und zB alle Pages seines Adressraum auf Lesen/Schreiben/Ausführen setzen könnte.

    Die Pages werden nicht alle Initialisiert und exisiteren meistens auch gar nicht alle. Man wählt für das Paging beim x86 meistens 4k Abschnitte (die andere Möglichkeit waren glaube ich 2 MB Abschnitte), das ergibt für den 4 gb Adressraum dann 1 Millionen Einträge, die je 4 Byte groß sind. Also würden dafür rund 4 MB pro Prozess draufgehen. In der damaligen Zeit war das nicht akzeptabel. Deswegen sind die Page Einträge hierarchisch. In einem Directory stehen 1024 Verweise auf Tabellen, die je 1024 Pages enthalten. Das Directory exisitert auf jeden Fall, aber statt dem Verweis auf eine Tabelle kann dort einfach eine Markierung stehen das diese Fehlt. Statt der Page kann natürlich auch wieder das Fehlen dieser markiert sein.

    Was aber eigentlich der springende Punkt dabei ist, die Paging Daten liegen im (physischen) Speicher. Das Betriebsystem kann da also immer etwas ändern. Ein Prozess kommt an diese Struktur aber nur dran wenn das Betriesystem diesen Speicher in den virtuellen Adressraum mappt. Und das wird es nicht tun.

    MatheStein schrieb:

    Könnte ich jetzt einfach nach belieben eine Page beschreiben und der "initiale Pagefault" für diese Page sorgt dann dafür, dass eine Kachel der zu beschreibenden Page zugeordnet wird?
    Oder muss initial erst noch dem BS explizit mitgeteilt werden, dass man zB auf die Page mit Adresse 0x12345678 schreiben will und somit die Page mit einer syscall erst zugänglich machen?

    Es wäre auf jeden Fall beides möglich. Unter Windows und Linux wird dein Programm aber beendet wenn du auf eine Adresse zugreifst die dir nicht gemappt wurde. Das ist dieses beleibte "XY hat ein Problem festgestellt und muss beendet werden" unter Windows und der segfault (In dem Namen steckt Segment, es ist aber nur ein Name der übernommen wurde 🙂 ) bei Linux. Wahlos auf eine Adresse zugreifen deutet auch eher auf einen Fehler hin statt geplantes verhalten.

    Ich weiß allerdings nicht ob es Möglichkeiten gibt Speicher an einer bestimmten virtuellen Adresse zu bekommen. Dem Programm kann es eigentlich egal sein, an welche Adresse der Speicher liegt. Beim malloc gibt man ja auch nur die Größe an und arbeitet dann mit der zurückgegebenen Adresse.



  • vielen dank für die ausführliche Antwort 🙂

    weißt du zufällig auf welche systemaurufe sich malloc() stützt?

    gruß



  • Tobiking2 schrieb:

    Ich weiß allerdings nicht ob es Möglichkeiten gibt Speicher an einer bestimmten virtuellen Adresse zu bekommen.

    unter windoof geht es z.b. damit: http://msdn.microsoft.com/en-us/library/aa366887(VS.85).aspx
    btw, zugriff auf eine virtuelle adresse, ohne vorher 'VirtualAlloc' aufgerufen zu haben, führt zum crash. ich glaub, da hab' ich vorhin was falsches erzählt.

    MatheStein schrieb:

    weißt du zufällig auf welche systemaurufe sich malloc() stützt?

    windosen: HeapCreate, HeapAlloc usw. (nehme ich an, VirtualAlloc wär aber auch möglich).
    unixoide: brk und sbrk
    🙂



  • dachte eher an mmap() munmap() usw...??



  • ich glaube jetzt endlich habe ich das ganze einigermaßen verstanden 🙂

    Nur nochmal zur Sicherheit:
    Wenn zB wie hier
    http://www.fbi.h-da.de/~a.schuette/Vorlesungen/Unix/Skript/C-Programmierung/Systemaufrufe.pdf
    auf Seite 11 von Segmenten die Rede ist, dann sind damit nicht wirkliche Segmente (aus Hardwaresicht) gemeint, sondern Bereiche, die vom BS festgelegt und verwaltet werden oder? 🙂

    Wenn ich jetzt zB unter Linux "sbrk" aufrufe, erweitert das BS den Heap dann direkt um 4MB Speicher indem es eine weiter Pagetable verfügbar macht?

    Und wann genau bekomme ich jetzt unter Linux eine "Segmentation fault"?
    So wie ich das verstanden habe nur, wenn die entsprechende Pagetable, der die angesprochenen Adresse zugeordnet ist, nicht in der Directory-Tabelle verfügbar ist oder?

    Demnach würde ich keine "Segmenation fault" erhalten, wenn die entsprechende Pagetable verfügbar ist, nur die entsprechende Page, auf die sich die Adresse bezieht ausgelagert bzw noch gar nicht verwendet wurde oder?

    Ist abschließend richtig zu sagen, dass Pagetabeles mit Hilfe von brk/sbrk eingelagert werden und Pages automatisch mit dem Pagefault-Interrupt?

    Vielen dank nochmal für die vielen Hilfen :):)

    Gruß und schönes Wochenende



  • Hallo,

    vielleicht noch ein paar Worte zur Segmentierung unter x86:
    Die Segmentierung ist im 32Bit Protected-Mode immer noch präsent aber sie wird vom OS ausgetrickst in dem alle Segmente auf die Basis 0 und Größe 4 GB gesetzt werden. Alle Segmente die ein User-Mode-Prozess benutzt zeigen also auf den selben (gesamten) virtuellen Adressraum und damit wirkt es so als währe die Segmentierung nicht vorhanden.
    Offset (Dein 32Bit-Pointer) + Segment-Basis (üblicherweise 0) = virtuelle Adresse;
    da Segment-Basis 0 ist Offset = virtuelle Adresse.
    x + 0 = v >>> x = v

    Üblicherweise kann ein User-Mode-Prozess nicht einfach so in den virtuellen Speicher reingreifen ohne ihn vorher vom OS mappen zu lassen (das bedeutet nicht dass das OS tatsächlich echten Speicher alloziert es kann auch einfach nur vermerken das eine bestimmte Page für das Programm zugänglich ist aber noch nie benutzt wurde und diese dann beim ersten Zugriff, welcher eine Exception auslöst, erst mit echtem Speicher hinterlegen). Außnahme ist der Stack, dafür wird ein bestimmter virtueller Adressraum (sagen wir mal 4 MB von 0x91000000 bis 0x913FFFFF und Stack-Pointer auf 0x913FFFF0) reserviert. Dabei wird beim Prozessstart erstmal nur die 4k-Page an 0x913FF000 tatsächlich mit Speicher gemappt damit der Prozess anfangen kann ohne gleich eine Exception zu erzeugen. Alle weiteren Pages in diesem Stack werden vom OS automatisch beim ersten Zugriff gemappt damit der Prozess sich nicht auch noch um die Speicherzuweisung im Stack kümmern muss. Guarded-Pages hab ich jetzt mal der Einfachheit halber ignoriert.

    Beim Heap muss der Prozess dem OS schon explizit sagen wenn er mehr braucht, üblicherweise wird er dabei auch gleich einige MB anfordern welche dann in den Pool kommen aus dem malloc() sich bedient.

    MatheStein schrieb:

    Wenn ich jetzt zB unter Linux "sbrk" aufrufe, erweitert das BS den Heap dann direkt um 4MB Speicher indem es eine weiter Pagetable verfügbar macht?

    Ja, aber es muss nicht unbedingt in 4 MB Schritten erfolgen.

    MatheStein schrieb:

    Und wann genau bekomme ich jetzt unter Linux eine "Segmentation fault"?

    Wenn Du irgendwo in den virtuellen Adressraum reingreifst wo nichts ist oder wo Du nicht darfst. Eine Exception wird auch ausgelößt wenn die Page auf die Festplatte ausgelagert wurde aber das ist kein Fehler im Prozess sondern wird vom OS transparent abgehandelt.

    Grüße
    Erik



  • danke das war sehr hilfreich 🙂

    Wenn jetzt ein neure Prozess gestartet wird, wer sorgt denn für die Einteilung der logischen Abschnitte (Stack, Heap etc) im virt Adressraum dieses Prozesses?
    Das müsste eigentlich die Aufgabe des BS sein oder?
    Wie erfährt der C-Compiler jetzt aber, in welchem Adressbereich sich der Heap befindet um die allokationen (zB via "malloc" im code) vernüftig zu manegen? Allokationen geschehen ja wie ich verstanden habe zur Laufzeit, aber dem Compiler wird soweit ich weiß keine syscall, ähnlich malloc, vom BS zur Verfügung gestellt der und mit "brk" kommt man auch nicht weiter, da dieser nur den Heap vergrößert.

    Gruß 🙂



  • Hallo,

    MatheStein schrieb:

    Wenn jetzt ein neure Prozess gestartet wird, wer sorgt denn für die Einteilung der logischen Abschnitte (Stack, Heap etc) im virt Adressraum dieses Prozesses? Das müsste eigentlich die Aufgabe des BS sein oder?

    Das ist eine klar definierte Festlegung vom OS welche der Compiler berücksichtigen muss wenn er den Code erstellt.
    Allerdings wird das heutzutage, im Rahmen der "Address-Space-Layout-Randomization" http://de.wikipedia.org/wiki/Address_Space_Layout_Randomization, wieder aufgeweicht aber das ist fürs erste Verständnis nicht relevant.

    MatheStein schrieb:

    Wie erfährt der C-Compiler jetzt aber, in welchem Adressbereich sich der Heap befindet um die allokationen (zB via "malloc" im code) vernüftig zu manegen?

    Das weiß ich ehrlich gesagt nicht so genau wie das unter den üblichen OS genau gemacht wird. Vielleicht fragt der libc-Code (der malloc() anbietet) per SYSCALL beim OS nach oder diese Info wird irgendwie als "Parameter" mitgegeben.

    MatheStein schrieb:

    Allokationen geschehen ja wie ich verstanden habe zur Laufzeit, aber dem Compiler wird soweit ich weiß keine syscall, ähnlich malloc, vom BS zur Verfügung gestellt der und mit "brk" kommt man auch nicht weiter, da dieser nur den Heap vergrößert.

    Doch "brk" ist so ne Art malloc(), eben weil er den Heap vergrößert. malloc() liefert nur Speicher aus dem Heap bzw. der Heap dient nur dazu vom Prozess (per malloc()) in kleine Stückchen zerlegt zu werden welche dann im Code irgendwie benutzt werden dürfen. Normale statische Variablen liegen in der Data-Section (das Wort Section wird eher für diese Bereiche im virtuellem Adressraum benutzt).

    Grüße
    Erik



  • alles klar 🙂

    1000dank für die vielen Hilfen! Habt echt zu einem super Thread hier im Forum beigetragen


Anmelden zum Antworten