7

Ok Mam trochę pytanie ucznia noob.W jaki sposób kompilator układa kod w pamięci?

Więc wiem, że stosy zawierają wywołania podprogramów, a sterty zawierają struktury danych o zmiennej długości, a globalne zmienne statyczne są przypisywane do lokalizacji pamięci trwałej.

Ale jak to wszystko działa na mniej teoretycznym poziomie?

Czy kompilator zakłada, że ​​ma cały region pamięci od adresu 0 do nieskończoności? A potem po prostu zacznij przypisywać rzeczy?

A gdzie układa instrukcje, stos i stertę? U góry obszaru pamięci koniec obszaru pamięci?

Jak to działa z pamięcią wirtualną? Pamięć wirtualna jest przezroczysta dla programu?

Przepraszam za pytania bajułkowe, ale zajmuję się strukturami języka programowania i ciągle odnoszę się do tych regionów i chcę je zrozumieć na bardziej praktycznym poziomie.

DZIĘKI dużo wcześniej!

+0

Czy mogę poprosić o szybkie sprawdzenie? Czy jest to podstawowa maszyna do wywoływania funkcji, tworzenia ich zmiennych lokalnych i dodawania ich do stosu zawartego w programie podczas kompilacji? Czy maszyna jest częścią systemu operacyjnego? –

Odpowiedz

5

Kompleksowe wyjaśnienie prawdopodobnie wykracza poza zakres tego forum. Całe teksty są poświęcone temu tematowi. Jednak na uproszczonym poziomie można na to patrzeć w ten sposób.

Kompilator nie układa kodu w pamięci. Zakłada się, że ma cały obszar pamięci sam w sobie. Kompilator generuje pliki obiektowe, w których symbole w plikach obiektowych zwykle zaczynają się od przesunięcia 0.

Łącznik odpowiedzialny jest za przeciągnięcie plików obiektów razem, łącząc symbole z ich nową odsuniętą lokalizacją w obrębie połączonego obiektu i generując format pliku wykonywalnego .

Łącznik nie umieszcza również kodu w pamięci. Pakuje kod i dane do sekcji zwykle oznaczonych .text dla instrukcji kodu wykonywalnego i .data dla rzeczy takich jak zmienne globalne i stałe łańcuchowe. (i istnieją również inne sekcje do różnych celów) Łącznik może dostarczyć wskazówkę do programu ładującego systemu operacyjnego, gdzie można przenieść symbole, ale program ładujący nie musi tego robić.

Jest to program ładujący system operacyjny, który analizuje plik wykonywalny i decyduje o rozmieszczeniu kodu i danych w pamięci. Jego lokalizacja zależy całkowicie od systemu operacyjnego. Zazwyczaj stos znajduje się w wyższym regionie pamięci niż instrukcje programu i dane i rośnie w dół.

Każdy program jest skompilowany/połączony z założeniem, że ma całą swoją przestrzeń adresową. W tym miejscu pojawia się pamięć wirtualna. Jest całkowicie przezroczysta dla programu i zarządzana wyłącznie przez system operacyjny.

Pamięć wirtualna zazwyczaj waha się od adresu 0 i maksimum adresu obsługiwanego przez platformę (nie nieskończoność). Ta wirtualna przestrzeń adresowa jest podzielona przez system operacyjny na przestrzeń adresowalną jądra i przestrzeń adresowalną użytkownika. Powiedzmy na hipotetycznym 32-bitowym systemie operacyjnym, adresy powyżej 0x80000000 są zarezerwowane dla systemu operacyjnego, a poniższe adresy są używane przez program. Jeśli program spróbuje uzyskać dostęp do pamięci powyżej tej partycji, zostanie przerwany.

System operacyjny może zdecydować, że stos zaczyna się od największej adresowalnej pamięci użytkownika i rośnie wraz z kodem programu umieszczonym pod znacznie niższym adresem.

Położenie sterty jest zazwyczaj zarządzane przez bibliotekę wykonawczą, na podstawie której zbudowano program. Może zacząć żyć od następnego dostępnego adresu po kodzie programu i danych.

+0

To jest świetne. Dokładnie tego szukałem. Wszelkie zalecenia dotyczące dobrych witryn/książek na ten temat? Nie wiem nawet, co to google. Nie mam na myśli parse drzew, ale więcej o tym, jak wszystko to działa praktycznie na niskim poziomie podczas konwersji drzewa parse do kodu. –

+0

Dobrym miejscem na rozpoczęcie byłoby wyszukiwanie w Google "układu pamięci w systemie Windows". Ten link pojawia się na pierwszej stronie i wygląda na dość interesujący: [Anatomia programu w pamięci] (http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory) . Stąd prawdopodobnie możesz znaleźć inne koncepcje do google. –

+0

Hej, to świetnie! Dzięki jeszcze raz. –

1

To zależy.

Jeśli kompilujesz bootloader, który musi zacząć od zera, możesz założyć, że masz całą pamięć dla siebie.

Z drugiej strony, jeśli kompilujesz aplikację, możesz założyć, że masz całą pamięć dla siebie.

Niewielka różnica polega na tym, że w pierwszym przypadku masz całą pamięć fizyczną dla siebie. Jako bootloader nie ma jeszcze nic w pamięci RAM. W drugim przypadku system operacyjny znajduje się w pamięci, ale normalnie skonfiguruje dla ciebie pamięć wirtualną, dzięki czemu wydaje się, że masz całą przestrzeń adresową dla siebie. Oczywiście, wciąż musisz zapytać system operacyjny o rzeczywistą pamięć.

To ostatnie oznacza, że ​​system operacyjny narzuca pewne zasady. Na przykład. OS bardzo chciałby wiedzieć, gdzie jest pierwsza instrukcja twojego programu. Prosta reguła może polegać na tym, że twój program zawsze zaczyna się pod adresem 0, więc kompilator C może umieścić tam int main(). System operacyjny zazwyczaj chciałby wiedzieć, gdzie jest stos, ale jest to już bardziej elastyczna reguła. Jeśli chodzi o "stertę", system operacyjny naprawdę nie mógł się tym przejmować.

+0

Dzięki! Można więc bezpiecznie założyć, że masz całą pamięć dla siebie. –

2

To otwarte pytanie z wieloma tematami.

Zakładając typowy kompilator -> asembler -> linker toolchain. Kompilator nie wie dużo, po prostu koduje względne stosy, nie dba o to, ile i gdzie jest stos, to jest cel/piękno stosu, nie obchodzi. Kompilator generuje assembler asembler jest zmontowany na obiekt, a następnie łącznik trwa informacji linker skrypt niektórych argumentów wiersza smakowych lub dowodzenia, że ​​powiedzieć, że szczegóły dotyczące miejsca w pamięci, kiedy

gcc hello.c -o hello 

instalacja binutils ma domyślny skrypt linkera, który jest dostosowany do twojego celu (Windows, Mac, Linux, cokolwiek używasz). Skrypt ten zawiera informacje o tym, gdzie zaczyna się przestrzeń programu, a następnie stamtąd wie, gdzie rozpocząć stertę (po tekście, danych i bss). Wskaźnik stosu jest prawdopodobnie ustawiony przez ten skrypt łącznika i/lub os zarządza nim w inny sposób. I to określa twój stos.

Dla systemu operacyjnego z mmu, który jest tym, co mają twoje okna i komputery przenośne oraz komputery stacjonarne i przenośne z Mac i BSD, tak, każdy program jest kompilowany przy założeniu, że ma własną przestrzeń adresową zaczynającą się od 0x0000, co nie oznacza, że Program jest połączony z uruchomieniem przy 0x0000, zależy to od systemu operacyjnego od tego, jakie są te reguły systemu operacyjnego, niektóre zaczynają się na przykład od 0x8000.

Dla aplikacji typu "desktop", gdzie jest to w pewnym stopniu pojedyncza liniowa przestrzeń adresowa z perspektywy programów, najprawdopodobniej najpierw pojawi się .text, potem albo .data, albo .bss, a potem po tym wszystkim sterty zostaną wyrównane w pewnym momencie po tym. Stos jednak ustawiony jest zwykle wysoko i działa, ale może to być procesor i specyficzny system operacyjny. ten stos zwykle znajduje się w widoku programów świata na szczycie jego pamięci.

Pamięć wirtualna jest w tym wszystkim niewidoczna - aplikacja zwykle nie zna pamięci wirtualnej ani jej nie obchodzi. jeśli i kiedy aplikacja pobiera instrukcję lub przesyła dane, przechodzi przez sprzęt skonfigurowany przez system operacyjny i konwertujący pomiędzy wirtualnym i fizycznym.Jeśli mmu wskazuje na błąd, co oznacza, że ​​miejsce nie zostało zmapowane na adres fizyczny, może to czasem być celowe, a następnie inne zastosowanie terminu "pamięć wirtualna". Ta druga definicja systemu operacyjnego może na przykład wziąć inną porcję pamięci, twoją lub kogoś innego, na przykład przenieść na dysk twardy, zaznaczyć, że inny fragment nie istnieje, a następnie oznaczyć swój kawałek jako posiadający barana, który następnie przepuścił wykonujesz, nie wiedząc, że przerwał ci jakiś baran, którego nie wiedziałeś, że musisz wziąć od kogoś innego. Twoja aplikacja przez projekt nie chce nic z tego wiedzieć, po prostu chce działać, system operacyjny zajmuje się zarządzaniem pamięcią fizyczną i mmu, która daje wirtualną (zerową) przestrzeń adresową ...

Jeśli Miałem zrobić trochę nagiego programowania metalu, bez początkowo MMU, potem później, mikrokontrolera, qemu, raspberry pi, beaglebone, itd. Możesz zabrudzić sobie ręce kompilatorem, skryptem linkera i konfiguracją mmu. Używałbym ramienia lub mipów dla tego nie x86, aby ułatwić Ci życie, ogólny duży obraz przekłada się bezpośrednio na cele.

+0

Dzięki! Jedną rzeczą, która mnie wyrzuciła, było to, że musisz zadeklarować adres do przechowywania zmiennej w zestawie, więc pomyślałem, że generator kompilatora/zespołu założył, że ma swoją własną pełną przestrzeń adresową. –

Powiązane problemy