Risposta breve: il numero di indirizzi disponibili è uguale al più piccolo di quelli:
- Dimensione della memoria in byte
- Il più grande numero intero senza segno che può essere salvato nella parola macchina della CPU
Risposta lunga e spiegazione di quanto sopra:
La memoria è composta da byte (B). Ogni byte è composto da 8 bit (b).
1 B = 8 b
1 GB di RAM è in realtà 1 GiB (gibibyte, non gigabyte). La differenza è:
1 GB = 10^9 B = 1 000 000 000 B
1 GiB = 2^30 B = 1 073 741 824 B
Ogni byte di memoria ha il suo indirizzo, indipendentemente dalla grandezza della parola macchina CPU. Per esempio. La CPU Intel 8086 era a 16 bit e indirizzava la memoria per byte, così come le moderne CPU a 32 e 64 bit. Questa è la causa del primo limite: non puoi avere più indirizzi dei byte di memoria.
L'indirizzo di memoria è solo un numero di byte che la CPU deve saltare dall'inizio della memoria per arrivare a quello che sta cercando.
- Per accedere al primo byte deve saltare 0 byte, quindi l'indirizzo del primo byte è 0.
- Per accedere al secondo byte deve saltare 1 byte, quindi il suo indirizzo è 1.
- (e così via...)
- Per accedere all'ultimo byte, la CPU salta 1073741823 byte, quindi il suo indirizzo è 1073741823.
Ora devi sapere cosa significa effettivamente a 32 bit. Come ho detto prima, ha le dimensioni di una parola automatica.
La parola macchina è la quantità di memoria utilizzata dalla CPU per contenere i numeri (nella RAM, nella cache o nei registri interni). La CPU a 32 bit utilizza 32 bit (4 byte) per contenere i numeri. Anche gli indirizzi di memoria sono numeri, quindi su una CPU a 32 bit l'indirizzo di memoria è composto da 32 bit.
Ora pensaci: se hai un bit, puoi salvare due valori su di esso: 0 o 1. Aggiungi un altro bit e hai quattro valori: 0, 1, 2, 3. Su tre bit, puoi salvare otto valori : 0, 1, 2 ... 6, 7. Questo è in realtà un sistema binario e funziona così:
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
Funziona esattamente come la solita aggiunta, ma la cifra massima è 1, non 9. Decimale 0 è 0000
, quindi aggiungi 1 e ottieni 0001
, aggiungi ancora una volta e hai 0010
. Quello che succede qui è avere il decimale 09
e aggiungerne uno: cambi da 9 a 0 e incrementi la cifra successiva.
Dall'esempio sopra puoi vedere che c'è sempre un valore massimo che puoi tenere in un numero con un numero costante di bit - perché quando tutti i bit sono 1 e provi ad aumentare il valore di 1, tutti i bit diventeranno 0, interrompendo così il numero. Si chiama overflow dei numeri interi e causa molti problemi spiacevoli, sia per gli utenti che per gli sviluppatori.
11111111 = 255
+ 1
-----------
100000000 = 0 (9 bits here, so 1 is trimmed)
- Per 1 bit il valore più grande è 1,
- 2 bit - 3,
- 3 bit - 7,
- 4 bit - 15
Il numero più grande possibile è sempre 2 ^ N-1, dove N è il numero di bit. Come ho detto prima, un indirizzo di memoria è un numero e ha anche un valore massimo. Ecco perché la dimensione della parola macchina è anche un limite per il numero di indirizzi di memoria disponibili - a volte la tua CPU non è in grado di elaborare numeri abbastanza grandi da indirizzare più memoria.
Quindi su 32 bit puoi mantenere numeri da 0 a 2 ^ 32-1, e questo è 4 294 967 295. È più dell'indirizzo più grande nella RAM da 1 GB, quindi nel tuo caso specifico la quantità di RAM sarà il fattore limitante.
Il limite di RAM per CPU a 32 bit è teoricamente 4 GB (2 ^ 32) e per CPU a 64 bit è 16 EB (exabyte, 1 EB = 2 ^ 30 GB). In altre parole, la CPU a 64 bit potrebbe indirizzare l'intera Internet ... 200 volte;) (stimata da WolframAlpha ).
Tuttavia, nei sistemi operativi reali le CPU a 32 bit possono indirizzare circa 3 GiB di RAM. Ciò è dovuto all'architettura interna del sistema operativo: alcuni indirizzi sono riservati per altri scopi. Puoi leggere di più su questa cosiddetta barriera da 3 GB su Wikipedia . È possibile aumentare questo limite con l'estensione dell'indirizzo fisico .
Parlando di indirizzamento della memoria, ci sono alcune cose che dovrei menzionare: memoria virtuale , segmentazione e paging .
Memoria virtuale
Come ha sottolineato @Daniel R Hicks in un'altra risposta, i sistemi operativi utilizzano la memoria virtuale. Ciò significa che le applicazioni in realtà non funzionano su indirizzi di memoria reali, ma su quelli forniti dal sistema operativo.
Questa tecnica consente al sistema operativo di spostare alcuni dati dalla RAM a un cosiddetto Pagefile (Windows) o Swap (* NIX). L'HDD è di alcune dimensioni più lento della RAM, ma non è un problema serio per i dati a cui si accede raramente e consente al sistema operativo di fornire alle applicazioni più RAM di quelle effettivamente installate.
paging
Ciò di cui stavamo parlando finora si chiama schema di indirizzamento piatto.
Il paging è uno schema di indirizzamento alternativo che consente di indirizzare più memoria di quella normalmente possibile con una parola macchina in un modello piatto.
Immagina un libro pieno di parole di 4 lettere. Diciamo che ci sono 1024 numeri su ogni pagina. Per indirizzare un numero, devi sapere due cose:
- Il numero di pagina su cui è stampata quella parola.
- Quale parola su quella pagina è quella che stai cercando.
Questo è esattamente il modo in cui le moderne CPU x86 gestiscono la memoria. È diviso in 4 pagine KiB (1024 parole macchina ciascuna) e quelle pagine hanno numeri. (in realtà le pagine possono anche essere 4 MiB grandi o 2 MiB con PAE ). Quando si desidera indirizzare la cella di memoria, è necessario il numero di pagina e l'indirizzo in quella pagina. Si noti che ogni cella di memoria è referenziata da esattamente una coppia di numeri, che non sarà il caso della segmentazione.
Segmentazione
Bene, questo è abbastanza simile al paging. È stato utilizzato in Intel 8086, solo per citarne un esempio. I gruppi di indirizzi sono ora chiamati segmenti di memoria, non pagine. La differenza è che i segmenti possono sovrapporsi e si sovrappongono molto. Ad esempio, su 8086 la maggior parte delle celle di memoria erano disponibili da 4096 diversi segmenti.
Un esempio:
Diciamo che abbiamo 8 byte di memoria, tutti con zeri eccetto il 4 ° byte che è uguale a 255.
Illustrazione per modello di memoria piatta:
_____
| 0 |
| 0 |
| 0 |
| 255 |
| 0 |
| 0 |
| 0 |
| 0 |
-----
Illustrazione per memoria paginata con pagine a 4 byte:
PAGE0
_____
| 0 |
| 0 |
| 0 | PAGE1
| 255 | _____
----- | 0 |
| 0 |
| 0 |
| 0 |
-----
Illustrazione per memoria segmentata con segmenti di 4 byte spostati di 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 |
----- ----- ----- -----
Come puoi vedere, il 4 ° byte può essere indirizzato in quattro modi: (indirizzando da 0)
- Segmento 0, offset 3
- Segmento 1, offset 2
- Segmento 2, offset 1
- Segmento 3, offset 0
È sempre la stessa cella di memoria.
Nelle implementazioni nella vita reale i segmenti sono spostati di più di 1 byte (per 8086 erano 16 byte).
La cosa negativa della segmentazione è che è complicato (ma penso che tu già lo sappia;) Il bello è che puoi usare alcune tecniche intelligenti per creare programmi modulari.
Ad esempio puoi caricare un modulo in un segmento, quindi far finta che il segmento sia più piccolo di quello che è realmente (appena abbastanza piccolo da contenere il modulo), quindi scegliere il primo segmento che non si sovrappone a quello pseudo-piccolo e caricare il modulo successivo , e così via. Fondamentalmente ciò che ottieni in questo modo sono le pagine di dimensioni variabili.