Resposta curta: o número de endereços disponíveis é igual ao menor dos seguintes:
- Tamanho da memória em bytes
- Maior número inteiro não assinado que pode ser salvo na palavra de máquina da CPU
Resposta longa e explicação do acima exposto:
A memória consiste em bytes (B). Cada byte consiste em 8 bits (b).
1 B = 8 b
1 GB de RAM é na verdade 1 GiB (gibibyte, não gigabyte). A diferença é:
1 GB = 10^9 B = 1 000 000 000 B
1 GiB = 2^30 B = 1 073 741 824 B
Cada byte de memória tem seu próprio endereço, não importa o tamanho da palavra da máquina da CPU. Por exemplo. A CPU Intel 8086 tinha 16 bits e endereçava a memória por bytes, assim como as modernas CPUs de 32 e 64 bits. Essa é a causa do primeiro limite - você não pode ter mais endereços que bytes de memória.
O endereço da memória é apenas um número de bytes que a CPU precisa pular desde o início da memória para chegar ao que está procurando.
- Para acessar o primeiro byte, ele deve pular 0 bytes; portanto, o endereço do primeiro byte é 0.
- Para acessar o segundo byte, ele deve pular 1 byte, portanto seu endereço é 1.
- (e assim por diante...)
- Para acessar o último byte, a CPU ignora 1073741823 bytes, portanto seu endereço é 1073741823.
Agora você precisa saber o que realmente significa 32 bits. Como mencionei antes, é do tamanho de uma palavra de máquina.
Palavra de máquina é a quantidade de memória que a CPU usa para armazenar números (em RAM, cache ou registros internos). A CPU de 32 bits usa 32 bits (4 bytes) para armazenar números. Os endereços de memória também são números; portanto, em uma CPU de 32 bits, o endereço de memória consiste em 32 bits.
Agora pense sobre isso: se você tiver um bit, poderá salvar dois valores: 0 ou 1. Adicione mais um bit e você terá quatro valores: 0, 1, 2, 3. Em três bits, você pode salvar oito valores : 0, 1, 2 ... 6, 7. Este é realmente um sistema binário e funciona assim:
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
Funciona exatamente como a adição usual, mas o dígito máximo é 1, e não 9. O decimal 0 é 0000
, então você adiciona 1 e obtém 0001
, adiciona um mais uma vez e possui 0010
. O que aconteceu aqui é como ter decimal 09
e adicionar um: você altera 9 para 0 e incrementa o próximo dígito.
No exemplo acima, você pode ver que sempre há um valor máximo que você pode manter em número com número constante de bits - porque quando todos os bits são 1 e você tenta aumentar o valor em 1, todos os bits se tornam 0, quebrando assim o número. É chamado de excesso de número inteiro e causa muitos problemas desagradáveis, tanto para usuários quanto para desenvolvedores.
11111111 = 255
+ 1
-----------
100000000 = 0 (9 bits here, so 1 is trimmed)
- Para 1 bit, o maior valor é 1,
- 2 bits - 3,
- 3 bits - 7,
- 4 bits - 15
O maior número possível é sempre 2 ^ N-1, onde N é o número de bits. Como eu disse antes, um endereço de memória é um número e também tem um valor máximo. É por isso que o tamanho da palavra da máquina também é um limite para o número de endereços de memória disponíveis - às vezes, sua CPU não consegue processar números grandes o suficiente para endereçar mais memória.
Assim, em 32 bits, você pode manter os números de 0 a 2 ^ 32-1, e isso é 4 294 967 295. É mais do que o melhor endereço em 1 GB de RAM, portanto, no seu caso específico, a quantidade de RAM será o fator limitante.
O limite de RAM para a CPU de 32 bits é teoricamente 4 GB (2 ^ 32) e para a CPU de 64 bits é 16 EB (exabytes, 1 EB = 2 ^ 30 GB). Em outras palavras, a CPU de 64 bits pode endereçar a Internet inteira ... 200 vezes;) (estimada pela WolframAlpha ).
No entanto, nos sistemas operacionais da vida real, as CPUs de 32 bits podem endereçar cerca de 3 GiB de RAM. Isso ocorre devido à arquitetura interna do sistema operacional - alguns endereços são reservados para outros fins. Você pode ler mais sobre essa barreira de 3 GB na Wikipedia . Você pode aumentar esse limite com a extensão de endereço físico .
Falando sobre endereçamento de memória, há poucas coisas que devo mencionar: memória virtual , segmentação e paginação .
Memória virtual
Como @Daniel R Hicks apontou em outra resposta, os sistemas operacionais usam memória virtual. O que isso significa é que os aplicativos realmente não operam em endereços de memória reais, mas nos fornecidos pelo sistema operacional.
Essa técnica permite que o sistema operacional mova alguns dados da RAM para o chamado Pagefile (Windows) ou Swap (* NIX). O HDD é poucas magnitudes mais lentas que a RAM, mas não é um problema sério para dados raramente acessados e permite que o SO forneça aos aplicativos mais RAM do que você realmente instalou.
Paging
O que estávamos falando até agora é chamado de esquema de endereçamento simples.
A paginação é um esquema de endereçamento alternativo que permite endereçar mais memória do que você normalmente poderia com uma palavra de máquina no modelo plano.
Imagine um livro cheio de palavras de 4 letras. Digamos que existam 1024 números em cada página. Para endereçar um número, você precisa saber duas coisas:
- O número de páginas em que essa palavra é impressa.
- Qual palavra nessa página é a que você está procurando.
Agora é exatamente assim que as CPUs x86 modernas lidam com a memória. Está dividido em 4 páginas KiB (1024 palavras-máquina cada) e essas páginas têm números. (na verdade, as páginas também podem ter 4 MiB grandes ou 2 MiB com PAE ). Quando você deseja endereçar a célula de memória, precisa do número e endereço da página nessa página. Observe que cada célula de memória é referenciada por exatamente um par de números, que não será o caso da segmentação.
Segmentação
Bem, este é bem parecido com paginação. Foi usado no Intel 8086, apenas para citar um exemplo. Grupos de endereços agora são chamados de segmentos de memória, não de páginas. A diferença é que os segmentos podem se sobrepor e eles se sobrepõem muito. Por exemplo, no 8086, a maioria das células de memória estava disponível em 4096 segmentos diferentes.
Um exemplo:
Digamos que temos 8 bytes de memória, todos contendo zeros, exceto o 4º byte, que é igual a 255.
Ilustração para o modelo de memória plana:
_____
| 0 |
| 0 |
| 0 |
| 255 |
| 0 |
| 0 |
| 0 |
| 0 |
-----
Ilustração para memória paginada com páginas de 4 bytes:
PAGE0
_____
| 0 |
| 0 |
| 0 | PAGE1
| 255 | _____
----- | 0 |
| 0 |
| 0 |
| 0 |
-----
Ilustração para memória segmentada com segmentos de 4 bytes alterada em 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 |
----- ----- ----- -----
Como você pode ver, o quarto byte pode ser endereçado de quatro maneiras: (endereçando de 0)
- Segmento 0, deslocamento 3
- Segmento 1, deslocamento 2
- Segmento 2, deslocamento 1
- Segmento 3, deslocamento 0
É sempre a mesma célula de memória.
Nas implementações da vida real, os segmentos são deslocados em mais de 1 byte (para 8086 eram 16 bytes).
O que é ruim na segmentação é que é complicado (mas acho que você já sabe disso;) O que é bom é que você pode usar algumas técnicas inteligentes para criar programas modulares.
Por exemplo, você pode carregar algum módulo em um segmento, fingir que o segmento é menor do que realmente é (apenas pequeno o suficiente para reter o módulo), escolher o primeiro segmento que não se sobrepõe ao pseudo-menor e carregar o próximo módulo , e assim por diante. Basicamente, o que você obtém dessa maneira são páginas de tamanho variável.