Zeigerwerte in C
-
MatheStein schrieb:
Dabei müsste die Adresse von x (also &x) realtiv zum Segmentanfang des Stack-Segements in dem virt. Adressraum des Prozesses sein, genau wie die Adresse des per malloc angeforderten Speichers auch relativ zum Anfang des Heap-Segments sein müsste.
Nein, die Adresse von &x ist relativ zum gesamten virtuellen Adressraum der Applikation (der bei Adresse 0x0 beginnt) fest. Die von Dir angesprochene "Segmentierung" ist nur eine Festlegung, dass der Heap z.B. bei 0x08000000 und der Stack bei 0x1000000 beginnt.
-
MatheStein schrieb:
Wenn ich einen Prozess erzeuge, bekomme ich ja vom BS einen virtuellen Adressraum für diesen Prozess.
Dieser virtuelle Adressraum ist ja nun auch wieder in Segmente unterteilt(Stack-Segement, Heap-Segment, Data-Segement etc.).klar, aber die liegen irgendwo im 32-bittigen flachen adressraum und haben nichts mit x86-segmentierung zu tun.
MatheStein schrieb:
Wenn ich nun zB eine lokale Variable "int x" in meinem Programm anlege, wird diese auf dem Stack gespeichert (im Stack-Segment, des virt. Adressraums).
im stack-segment des threads (mal auf windoof bezogen). jeder thread hat einen eigenen stack. alle stacks aller threads eines prozesses sind im selben virtuellen adressraum.
MatheStein schrieb:
Fordere ich nun per "malloc()" speicher an, wird dieser im Heap reserviert (im Heap-Segment des virt.Adressraums).
der heap ist auch 'irgendwo', jeder prozess bekommt beim start einen heap verpasst. ein prozess kann auch mehrere heaps haben (gilt wieder für windosen).
MatheStein schrieb:
Dabei müsste die Adresse von x (also &x) realtiv zum Segmentanfang des Stack-Segements in dem virt. Adressraum des Prozesses sein, genau wie die Adresse des per malloc angeforderten Speichers auch relativ zum Anfang des Heap-Segments sein müsste.
stacks 'wachsen' normerweise von grossen nach kleinen adressen. heap-blöcke können 'irgendwo' im für den heap reservierten speicher sein, die anordnung regelt der heap-code, muss also nicht linear sein, so wie beim stack.
MatheStein schrieb:
Von daher wundert es mich, das C mir zB für das x oben nur eine 32bit Adresse (auf einem 32bit System) liefert ohne Bezug auf das zugehörige Segment.
weil eine 32-bittige adressangabe in einem 32-bit adressraum völlig eindeutig ist, du brauchst keine weiteren infos.
MatheStein schrieb:
Angenommen x liegt an stelle 0x00000001(=&x) im Stack-Segment. Dann könnte zB ein int* p=malloc(1) auch ein 0x00000001(=p) als return-Wert haben.
nein, falls einer der stacks bei 1 beginnen würde, wäre die adresse 1 (und ein paar adressen mehr, weil der stack ja nicht nur 1 byte gross ist), schon für den stack reserviert.
MatheStein schrieb:
Das würde bedeuten, dass x im Stack-Segment an erster Stelle ist und das malloc(1), den ersten Byte im Heap-Segment angefordert hat. Einzig Unterschied müsste der Segmentzeiger sein, den ich aber nun in C irgendwie vermisse.
weil im flat-mode keiner benötigt wird. C kennt sowieso keine konzepte wie segmentierung u.ä. beides passt also prima zusammen.
MatheStein schrieb:
Hoffe ihr versteht was ich meine und könnt mir sagen wo mein Fehler ist
vielleicht bringst du das irgendwie mit virtuellem speichermanagement durcheinander? das ist aber ein anderes thema.
-
danke für die antworten
leider bin ich jetzt etwas verwirrt.
http://pvs.uni-muenster.de/pvs/lehre/WS09/bs/folien/bsuebung8-4.pdf
guckt euch mal diese pdf an.
Auf Blatt3 Folie 5 ist der Adressraum eines Prozesses dargestellt in seine einzelnen Segmente unterteilt.
Meint ihr mit Segmente auch diese Segmente?
Gruß
-
Ein paar Folien weiter siehst Du das Layout des virtuellen Adressraums. Genau diese Adressen bekommst Du in C. Sprich der Stack fängt einfach nicht bei 0 an, sondern bei 0xbfxxxxxx...
Segment ist hier nur ein Begriff für "Abschnitt" und hat rein garnichts mit klassischer x86-Realmode-Segmentierung zu tun!
-
jo genau! Die Segmente aus dem real mode meinte ich jetzt auch die ganze zeit über NICHT.
http://pvs.uni-muenster.de/pvs/lehre/WS09/bs/folien/bsuebung7-4.pdf
hier sieht man auf seite auf Seite 6 Folie 8 wie der Pentium adressiert.
Der bekommt erst Segementselektor und Offset übergeben und bildet daraus eine Adresse aus dem virt. Adressraum (lineare Adresse, also einen Verweis auf die Page) die er dann wiederum erst in eine physikalische Adresse umrechnet.
In C erscheint es mir so, dass der Compiler sich komplett alleine mit der Segmentierung rumschlägt. Ist das so?
Also wenn ich zB folgenden Codeausschnitt habe:
int x; int* p = (int*) malloc(1); printf("Adresse x: %p, Adresse malloc: %p",&x,p);
Wie genau gibt der Adressen in dem printf zurück?
Zb &x? Für den Compiler müsste die Adresse ja aus "Segement Selektor" und Offset bestehen. Berechnet der daraus intern die lineare Adresse und gibt diese dann zrück um den nutzer die arbeit zu erleichtern?
gruß
-
MatheStein schrieb:
In C erscheint es mir so, dass der Compiler sich komplett alleine mit der Segmentierung rumschlägt. Ist das so?
nee, der compiler weiss nicht das geringste davon. beim 'flat memory model' dieser x86-teile haben die segmentregister einen festen wert und müssen nie geändert und auch nicht benutzt werden (ausser im kernel vielleicht, aber sowas muss bewusst geschehen, ein c-compiler weiss nichts davon). im prinzip läuft die gesamte im user-mode adressierung über den 32bit-'offset'.
MatheStein schrieb:
Wie genau gibt der Adressen in dem printf zurück?
mach doch: sizeof(p), dann siehste ja, dass es nur 32 bits sind.
-
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...??