A memória (ou memória principal) consiste num grande array de bytes cada um com o seu próprio endereço.
- A unidade de memória vê apenas um fluxo de endereços de memória e não sabe como são gerados ou para que servem.
Memória e Registos são o único tipo de armazenamento a que o CPU pode aceder diretamente.
- Os programas devem ser trazidos do disco para a memória para ser corridos.
- O CPU, em seguida, recolhe as instruções da memória de acordo com o counter do programa.
- Estas instruções podem causar carregamento/armazenamento adicional de/para endereços de memória específicos.
A maioria dos CPUs consegue descodificar as instruções e realizar operações simples em registos à taxa de uma ou mais operações por clock cycle.
A memória é acedida pelo memory bus o que pode levar alguns clock cycles do CPU.
A cache encontra-se entre a memória e os registos do CPU. Permite aumentar a velocidade de acesso à memória.
Uniprogramming
:
- As aplicações correm sempre no mesmo local na memória física;
- As aplicações podem aceder a qualquer endereço físico;
- Não é necessária a proteção da memória uma vez que apenas é executada um aplicação de cada vez.
Multiprogramming
:
-
As aplicações podem correr em diferentes localizações na memória fisica;
-
As aplicações não podem aceder a todos os endereções físicos (memória normalmente dividida em duas zonas: (i) sistema operativo, geralmente em memória baixa juntamente com o vetor de interrupt; (ii) processos de utilizador, geralmente em alta memória);
-
A proteção de memória é necessária para prevenir o overlap de endereços entre processos.
A proteção de memória é necessária para garantir um funcionamento correto.
- Proteger os processos do sistema operativo do acesso por processos do utilizador;
- Proteger processos do utilizador uns dos outros.
A proteção de memória deve ser fornecida pelo hardware.
- Normalmente, o Sistema Operativo não intervém entre o CPU e os seus acessos à memória devido à consequente penalização de desempenho.
Precisamos de garantir que cada processo tem um espaço separado em memória.
- Separar o espaço de memória por processo protege os processos uns dos outros e é fundamental para ter múltiplos processos carregados em memória para execução simultânea.
- Para separar o espaço em memória, precisamos da capacidade de determinar o leque de endereços legais a que o processo pode aceder.
Um par de registos define o espaço de endereço de um processo:
- O registo base contém o menor endereço legal da memória fisica.
- O registo limit especifica o tamanho.
Os utilizadores não têm permissão para alterar os registos base/limite.
Sem esta proteção, qualquer bug poderia levar ao crash de outros programas ou até mesmo do sistema operativo.
O conceito de um espaço de endereço lógico que está ligado a um espaço de endereço físico separado é fulcral para uma gestão adequada de memória.
Logical Address
: Endereço gerado pelo CPU;Physical Address
: Endereço visto pela unidade de memória;
O conjunto de todos os endereços lógicos gerados por um programa é o espaço de endereço lógico. O conjunto de todos os endereços físicos correspondentes a cada um dos endereços lógicos é o espaço de endereços físicos.
No tempo de compilação e de load, o "esquema" de ligação de endereços gera os mesmos endereços lógicos e físicos.
No tempo de execução, o "esquema" de ligação de endereços gera um endereço lógico e físico diferentes.
O dispositivo de hardware que no tempo de execução mapeia endereços lógicos para físicos é chamado de MMU.
- Existem diferentes métodos para completar tal mapeamento.
- Usa-se uma forma extrema de context switch onde alguns processos em memória são temporariamente trocados da memória para uma backing store.
- Torna possível que o espaço total de endereços fisicos de todos os processos possa exceder a verdadeira memória física do sistema.
Backing store
-> Geralmente um disco rápido e grande o suficiente para acomodar cópias de todas as imagens de memória para todos os utilizadores.
- A read queue mantém os processos ready-to-run dos quais têm imagens de mémoria na backing store.
Dispatcher
-> Verifica se um processo está agendado em memória para executar.
- Se não estiver e se não houver memória livre nessa região, o dispatcher troca o processo atual em memória pelo processo desejado.
- O tempo do Context-switch num sistema de swapping é bastante elevado.
Maior parte do tempo de swap é na verdade tempo de transferência (temos de trocar em ambos out e in)
- O tempo total de transferência é diretamente proporcional à quantidade de memória trocada.
Um processo trocado para a backing store precisa de ser colocado no mesmo endereço fisico de onde foi retirado?
- Não, caso tenha sido usada alocação dinâmica para mudar o relocation register.
- Pode, se usarmos double buffering e a opereção de I/O for executada dentro de buffers do kernel (transferências entre buffers do kernel e buffers de memória de processo, só ocorrem quando o processo é trocado back in.)
Alocação contígua de memória é um método inicial em que cada processo está contido numa única secção contígua de memória.
Um esquema simples é dividir a memória em varías partições de tamanho fixo.
- Cada partição contém exatamente 1 processo.
- O grau de multiprogramação está ligado pelo número de partições
A geralização é o esquema de partição variável
- Cada partição contém na mesma um único processo mas o tamanho da partição é exatamente aquele que o processo precisa.
- O Kernel mantém uma tabela que indica que partes da memória estão ocupadas e que partes estão disponíveis (memory holes).
Fragementação interna existe quando a memória alocada a um processo é ligeiramente maior do que memória solicitada.
- A diferença de tamanhos está relacionada com memória que não é usada que é interna à partição.
É o resultado de tentar evitar o overhead quando se mantém o rasto de pequenos buracos na memória.
Exemplo:
- Consideremos que temos um buraco na memória para 1000 bytes e o processo que está a pedir essa parte da memória necessita apenas de 998 bytes, cria-se um buraco pequeno que apenas tem espaço para 2 bytes -> é melhor ignorar esse pequeno buraco do que lidar com ele.
- Um aproximação geral para evitar pequenos espaços na memória é separar a memória fisica em blocos de tamanho fixo e alocar unidades de memória baseado no tamanho de cada bloco.
Fragementação externa acontece quando existe espaço suficiente na memória para satisfazer o pedido de alocação, no entanto, o espaço disponível não é contíguo.
- O armazenamento está fragmentado num número grande de buracos pequenos.
- No pior caso, podemos ter um espaço de dois em dois processos em toda a memória.
Uma possivel solução seria "misturar" (shuffle) a memória para compactar toda a que está livre num único espaço.
- Só é possivel com alocação dinâmica, feita em tempo de execução.
- Pode ser muito dispendioso.
Outra solução possivel seria usar esquemas de alocação de memória não contíguos.
-Segmentation
e Paging
são dois esquemas possíveis.
A memória é vista como uma coleção de unidades lógicas separadas e de tamanho variável sem nenhuma ordem necessária entre elas.
- Programa main
- Procedimentos/Funções
- Objetos/Métodos
- Variáveis locais e globais
- Stack
- Tabela de Símbolos
- Arrays