Krótka odpowiedź: liczba dostępnych adresów jest mniejsza od tych:
- Rozmiar pamięci w bajtach
- Największa liczba całkowita bez znaku, którą można zapisać w słowie maszyny CPU
Długa odpowiedź i wyjaśnienie powyższego:
Pamięć składa się z bajtów (B). Każdy bajt składa się z 8 bitów (b).
1 B = 8 b
1 GB pamięci RAM to w rzeczywistości 1 GiB (gibibajt, nie gigabajt). Różnica polega na:
1 GB = 10^9 B = 1 000 000 000 B
1 GiB = 2^30 B = 1 073 741 824 B
Każdy bajt pamięci ma swój adres, bez względu na to, jak duże jest słowo maszyny CPU. Na przykład. Procesor Intel 8086 był 16-bitowy i adresował pamięć bajtami, podobnie jak nowoczesne 32-bitowe i 64-bitowe procesory. To jest przyczyną pierwszego limitu - nie możesz mieć więcej adresów niż bajtów pamięci.
Adres pamięci to tylko liczba bajtów, które procesor musi pominąć od początku pamięci, aby dostać się do tego, którego szuka.
- Aby uzyskać dostęp do pierwszego bajtu, musi pominąć 0 bajtów, więc adres pierwszego bajtu to 0.
- Aby uzyskać dostęp do drugiego bajtu, musi pominąć 1 bajt, więc jego adres to 1.
- (i tak dalej...)
- Aby uzyskać dostęp do ostatniego bajtu, procesor pomija 1073741823 bajtów, więc jego adres to 1073741823.
Teraz musisz wiedzieć, co właściwie oznacza wersja 32-bitowa. Jak wspomniałem wcześniej, jest to wielkość słowa maszynowego.
Słowo maszynowe to ilość pamięci używanej przez procesor do przechowywania liczb (w pamięci RAM, pamięci podręcznej lub rejestrach wewnętrznych). 32-bitowy procesor wykorzystuje 32 bity (4 bajty) do przechowywania liczb. Adresy pamięci również są liczbami, więc na 32-bitowym procesorze adres pamięci składa się z 32 bitów.
Pomyśl o tym: jeśli masz jeden bit, możesz zapisać na nim dwie wartości: 0 lub 1. Dodaj jeszcze jeden bit i masz cztery wartości: 0, 1, 2, 3. Na trzech bitach możesz zapisać osiem wartości : 0, 1, 2 ... 6, 7. To jest w rzeczywistości system binarny i działa w ten sposób:
Decimal Binary
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
Działa dokładnie tak jak zwykłe dodawanie, ale maksymalna cyfra to 1, a nie 9. Liczba dziesiętna to 0 0000
, następnie dodajesz 1 i dostajesz 0001
, dodajesz jeszcze raz i masz 0010
. To, co się tutaj dzieje, jest po przecinku 09
i dodaniu jednego: zmieniasz 9 na 0 i zwiększasz kolejną cyfrę.
Z powyższego przykładu widać, że zawsze istnieje maksymalna wartość, którą można zachować w liczbie ze stałą liczbą bitów - ponieważ gdy wszystkie bity mają wartość 1, a Ty próbujesz zwiększyć wartość o 1, wszystkie bity staną się równe 0, co spowoduje przerwanie numer. Nazywa się to przepełnieniem liczb całkowitych i powoduje wiele nieprzyjemnych problemów, zarówno dla użytkowników, jak i programistów.
11111111 = 255
+ 1
-----------
100000000 = 0 (9 bits here, so 1 is trimmed)
- Dla 1 bitu największą wartością jest 1,
- 2 bity - 3,
- 3 bity - 7,
- 4 bity - 15
Największa możliwa liczba to zawsze 2 ^ N-1, gdzie N jest liczbą bitów. Jak powiedziałem wcześniej, adres pamięci jest liczbą i ma również wartość maksymalną. Dlatego rozmiar słowa maszynowego jest również ograniczeniem liczby dostępnych adresów pamięci - czasami twój procesor po prostu nie może przetworzyć liczb wystarczająco dużych, aby zająć więcej pamięci.
Na 32 bitach możesz przechowywać liczby od 0 do 2 ^ 32-1, a to 4 294 967 295. To więcej niż największy adres w 1 GB pamięci RAM, więc w twoim przypadku ilość pamięci RAM będzie czynnikiem ograniczającym.
Limit pamięci RAM dla 32-bitowego procesora wynosi teoretycznie 4 GB (2 ^ 32), a dla 64-bitowego procesora - 16 EB (eksabajtów, 1 EB = 2 ^ 30 GB). Innymi słowy, 64-bitowy procesor mógłby adresować cały Internet ... 200 razy;) (oszacowany przez WolframAlpha ).
Jednak w rzeczywistych systemach operacyjnych 32-bitowe procesory mogą obsługiwać około 3 GiB pamięci RAM. Wynika to z wewnętrznej architektury systemu operacyjnego - niektóre adresy są zarezerwowane do innych celów. Możesz przeczytać więcej o tak zwanej barierze 3 GB na Wikipedii . Możesz podnieść ten limit za pomocą rozszerzenia adresu fizycznego .
Mówiąc o adresowaniu pamięci, jest kilka rzeczy, o których powinienem wspomnieć: pamięć wirtualna , segmentacja i stronicowanie .
Pamięć wirtualna
Jak zauważył @Daniel R. Hicks w innej odpowiedzi, systemy operacyjne używają pamięci wirtualnej. Oznacza to, że aplikacje faktycznie nie działają na prawdziwych adresach pamięci, ale na tych dostarczonych przez system operacyjny.
Ta technika pozwala systemowi operacyjnemu przenieść niektóre dane z pamięci RAM do tak zwanego pliku stronicowania (Windows) lub zamiany (* NIX). Dysk twardy jest o kilka wielkości wolniejszy niż pamięć RAM, ale nie stanowi poważnego problemu w przypadku rzadko uzyskiwanych danych i umożliwia systemowi operacyjnemu dostarczenie aplikacji więcej pamięci RAM niż faktycznie zainstalowano.
Stronicowanie
To, o czym mówiliśmy do tej pory, nazywa się schematem adresowania płaskiego.
Stronicowanie jest alternatywnym schematem adresowania, który pozwala zaadresować więcej pamięci niż zwykle przy użyciu jednego słowa maszynowego w modelu płaskim.
Wyobraź sobie książkę wypełnioną 4-literowymi słowami. Powiedzmy, że na każdej stronie jest 1024 liczb. Aby zaadresować numer, musisz znać dwie rzeczy:
- Liczba stron, na których to słowo jest drukowane.
- Które słowo na tej stronie jest tym, którego szukasz.
Właśnie tak nowoczesne procesory x86 obsługują pamięć. Podzielony jest na 4 strony KiB (po 1024 słowa maszynowe), które mają numery. (w rzeczywistości strony mogą być również 4 MiB duże lub 2 MiB z PAE ). Aby zaadresować komórkę pamięci, potrzebujesz numeru strony i adresu na tej stronie. Zauważ, że do każdej komórki pamięci odwołuje się dokładnie jedna para liczb, co nie będzie miało miejsca w przypadku segmentacji.
Segmentacja
Ten jest bardzo podobny do stronicowania. Został użyty w Intel 8086, żeby wymienić tylko jeden przykład. Grupy adresów są teraz nazywane segmentami pamięci, a nie stronami. Różnica polega na tym, że segmenty mogą się nakładać i bardzo często się pokrywają. Na przykład w 8086 większość komórek pamięci była dostępna z 4096 różnych segmentów.
Przykład:
Powiedzmy, że mamy 8 bajtów pamięci, wszystkie z zerami, z wyjątkiem 4 bajtu, który jest równy 255.
Ilustracja modelu płaskiej pamięci:
_____
| 0 |
| 0 |
| 0 |
| 255 |
| 0 |
| 0 |
| 0 |
| 0 |
-----
Ilustracja pamięci stronicowanej z 4-bajtowymi stronami:
PAGE0
_____
| 0 |
| 0 |
| 0 | PAGE1
| 255 | _____
----- | 0 |
| 0 |
| 0 |
| 0 |
-----
Ilustracja podzielonej pamięci z 4-bajtowymi segmentami przesuniętymi o 1:
SEG 0
_____ SEG 1
| 0 | _____ SEG 2
| 0 | | 0 | _____ SEG 3
| 0 | | 0 | | 0 | _____ SEG 4
| 255 | | 255 | | 255 | | 255 | _____ SEG 5
----- | 0 | | 0 | | 0 | | 0 | _____ SEG 6
----- | 0 | | 0 | | 0 | | 0 | _____ SEG 7
----- | 0 | | 0 | | 0 | | 0 | _____
----- | 0 | | 0 | | 0 | | 0 |
----- ----- ----- -----
Jak widać, czwarty bajt można adresować na cztery sposoby: (adresowanie od 0)
- Segment 0, przesunięcie 3
- Segment 1, przesunięcie 2
- Segment 2, przesunięcie 1
- Segment 3, przesunięcie 0
To zawsze ta sama komórka pamięci.
W rzeczywistych implementacjach segmenty są przesuwane o więcej niż 1 bajt (dla 8086 było to 16 bajtów).
Co jest złe w segmentacji jest to, że jest skomplikowane (ale myślę, że już to wiesz;) Co dobre, to że możesz użyć sprytnych technik do tworzenia programów modułowych.
Na przykład możesz załadować jakiś moduł do segmentu, a następnie udawać, że segment jest mniejszy niż jest w rzeczywistości (wystarczy na tyle, aby pomieścić moduł), a następnie wybierz pierwszy segment, który nie zachodzi na ten pseudo-mniejszy i załaduj następny moduł , i tak dalej. Zasadniczo otrzymujesz w ten sposób strony o zmiennej wielkości.