Alocador de memória
Este projeto visa construir um alocador de memória heap para o PPOS. Com ele funcionando, não precisaremos mais usar o alocador da biblioteca C (malloc/free).
Estrutura geral
A área de memória heap, usada para alocação dinâmica, será representada em nosso SO por um grande vetor estático de bytes, a ser definido em memory.c:
// tamanho total da heap: 64 MB #define HEAP_SIZE 64*1024*1024 static char heap[HEAP_SIZE];
Essa área de memória poderá ser alocada pelas aplicações em blocos de tamanho variável, usando o algoritmo de alocação first-fit, que aloca sempre o primeiro bloco de memória livre com tamanho suficiente. A figura abaixo mostra a organização da memória heap, com blocos alocados e blocos livres:
Para gerenciar os blocos de memória (livres ou alocados), sugere-se definir um vetor de descritores de bloco contendo, para cada bloco:
- endereço inicial do bloco
- tamanho do bloco em bytes
- estado do bloco (livre ou alocado)
- número do bloco vizinho da esquerda
- número do bloco vizinho da direita
Algoritmo de alocação
A alocação de um bloco usa a seguinte estratégia:
- ajusta o tamanho solicitado para um múltiplo de 16 bytes
- na lista de blocos livres, procura um bloco com tamanho suficiente (first-fit)
- se não encontrar, retorna NULL
- se o tamanho do bloco livre encontrado for maior que o necessário, redimensiona-o e cria um novo bloco livre com a área excedente
- marca o bloco como ocupado
- retorna um ponteiro para o início do bloco
A figura a seguir ilustra a alocação consecutiva de 4 blocos de memória, em um heap inicialmente livre:
A liberação de um bloco usa a seguinte estratégia:
- localiza o número do bloco alocado a partir do endereço fornecido
- se não encontrar o bloco, retorna erro
- se o bloco já estiver livre, retorna erro
- marca o bloco como livre
- se o bloco à sua esquerda também estiver livre, funde-os em um só bloco
- se o bloco à sua direita também estiver livre, funde-os em um só bloco
Ao liberar um bloco, deve-se verificar se seus vizinhos imediatos estão livres. Se algum vizinho estiver livre, o bloco recém-liberado e seus vizinhos livres podem ser fundidos em um bloco livre maior. Esse processo, denominado coalescência e ilustrado abaixo, permite obter blocos livres maiores, diminuindo a fragmentação da memória.
Funções a implementar
As seguintes funções, com suas respectivas estruturas de dados de apoio, devem ser implementadas em memory.c:
Inicia a memória
void mem_init();
Inicia o subsistema de memória, preparando as estruturas de dados de controle do mesmo (tabelas/listas de descritores, etc.).
Aloca memória
void *mem_alloc(int size);
Aloca um bloco de memória com o tamanho indicado em bytes; retorna um ponteiro para a área alocada ou NULL se houver erro.
Cada bloco alocado deve ter um tamanho mínimo de 16 bytes e deve ser alinhado em 16 bytes, ou seja, seu tamanho e endereço inicial devem ser múltiplos de 16. O ajuste de um valor para o próximo múltiplo de 16 pode ser feito assim:
// versão mais rápida value = ((value - 1) | 0x00000F) + 1; // versão mais simples while (value % 16) value++ ;
Libera memória
int mem_free(void *addr);
Libera um bloco de memória previamente alocado, indicado pelo endereço addr; retorna 0 se executar sem problemas ou -1 se o ponteiro for NULL ou indicar uma área não-alocada.
Caso algum bloco vizinho esteja livre, ele deve ser fundido ao bloco liberado.
Memória total
int mem_size();
Informa a quantidade de memória total, em bytes.
Memória livre
int mem_avail();
Informa a quantidade total de memória disponível, em bytes. Não se preocupa se essa memória livre está fragmentada ou não.
Informa sobre o uso da memória
void mem_report();
Imprime um relatório sobre o uso atual da memória, com o seguinte formato:
heap: 184 KB allocated in 11 blocks, 65351 KB available, 65536 KB total heap: block 1: 0x000055c9a1202340 - 0x000055c9a120537f used prev 0 next 2 size 12352 heap: block 2: 0x000055c9a1205380 - 0x000055c9a1206c3f used prev 1 next 3 size 6336 heap: block 3: 0x000055c9a1206c40 - 0x000055c9a12079af used prev 2 next 4 size 3440 heap: block 4: 0x000055c9a12079b0 - 0x000055c9a12082bf used prev 3 next 5 size 2320 heap: block 5: 0x000055c9a12082c0 - 0x000055c9a12161bf used prev 4 next 6 size 57088 heap: block 6: 0x000055c9a12161c0 - 0x000055c9a12227ff used prev 5 next 7 size 50752 heap: block 7: 0x000055c9a1222800 - 0x000055c9a1226f1f used prev 6 next 8 size 18208 heap: block 8: 0x000055c9a1226f20 - 0x000055c9a122ca7f used prev 7 next 9 size 23392 heap: block 9: 0x000055c9a122ca80 - 0x000055c9a122feff used prev 8 next 10 size 13440 heap: block 10: 0x000055c9a122ff00 - 0x000055c9a123035f used prev 9 next 11 size 1120 heap: block 11: 0x000055c9a1230360 - 0x000055c9a520233f FREE prev 10 next 0 size 66920416
Cada entrada do relatório deve informar:
- número do bloco
- endereços inicial e final
- status (livre ou ocupado)
- vizinho à esquerda e à direita
- tamanho em bytes
Observações:
Após a implementação do alocador, você deve usá-lo em todo o seu projeto. Nos arquivos kernel/*.c e lib/queue.c, você deve:
- trocar todas as ocorrências de
mallocpormem_alloc - trocar todas as ocorrências de
freepormem_free - remover todas as inclusões de
stdlib.h
Sua implementação deve funcionar corretamente com o programa de teste test/pingpong-mqueue.c. Esse programa vai usar intensivamente a alocação de memória na criação e destruição de filas genéricas, tarefas, semáforos e filas de mensagens.
Arquivos
Os seguintes arquivos são relevantes para este projeto:
kernel/memory.h: interface da gestão de memóriakernel/memory.c: implementação gestão de memóriatest/pingpong-memory.c: programa de testetest/pingpong-memory.txt: saída esperada do testetest/pingpong-mqueue.c: teste mais complexo
Outras informações
- Duração estimada: 6 horas.
- Dependências:


