Write a malloc?
malloc
Implémentation d’un malloc
simple
Pour implémenter un malloc simple:
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void *malloc(size_t size) {
void *p = sbrk(0);
void *request = sbrk(size);
if (request == (void*) -1) {
return NULL; // sbrk failed.
} else {
assert(p == request); // Not thread safe.
return p;
}
}
Cette implémentation augmente la taille du tas (heap) à l’aide de sbrk et retourne un pointeur vers la nouvelle région. Cependant, elle manque de certaines fonctionnalités comme la gestion correcte de malloc(0) et la libération de mémoire.
Handling free
le prototype pour free
est:
void free(void *ptr);
La libération nécessite de suivre la taille des blocs alloués. Pour ce faire, nous pouvons utiliser une astuce : stocker des métadonnées sur les régions mémoire juste avant le pointeur retourné par malloc
. Voici comment :
struct block_meta {
size_t size;
struct block_meta *next;
int free;
int magic; // Debugging only.
};
#define META_SIZE sizeof(struct block_meta)
Gestion du tas (heap)
Nous maintenons une liste chaînée des blocs mémoire :
void *global_base = NULL;
struct block_meta *find_free_block(struct block_meta **last, size_t size) {
struct block_meta *current = global_base;
while (current && !(current->free && current->size >= size)) {
*last = current;
current = current->next;
}
return current;
}
struct block_meta *request_space(struct block_meta* last, size_t size) {
struct block_meta *block;
block = sbrk(0);
void *request = sbrk(size + META_SIZE);
assert((void*)block == request); // Not thread safe.
if (request == (void*) -1) {
return NULL; // sbrk failed.
}
if (last) {
last->next = block;
}
block->size = size;
block->next = NULL;
block->free = 0;
block->magic = 0x12345678;
return block;
}
Mise a jour de malloc
Notre version mise à jour de malloc intègre la réutilisation des blocs :
void *malloc(size_t size) {
struct block_meta *block;
if (size <= 0) {
return NULL;
}
if (!global_base) { // First call.
block = request_space(NULL, size);
if (!block) {
return NULL;
}
global_base = block;
} else {
struct block_meta *last = global_base;
block = find_free_block(&last, size);
if (!block) {
block = request_space(last, size);
if (!block) {
return NULL;
}
} else {
block->free = 0;
block->magic = 0x77777777;
}
}
return (block + 1);
}
Implementation de free
struct block_meta *get_block_ptr(void *ptr) {
return (struct block_meta*)ptr - 1;
}
void free(void *ptr) {
if (!ptr) {
return;
}
struct block_meta* block_ptr = get_block_ptr(ptr);
assert(block_ptr->free == 0);
assert(block_ptr->magic == 0x77777777 || block_ptr->magic == 0x12345678);
block_ptr->free = 1;
block_ptr->magic = 0x55555555;
}
Ajout de realloc
et calloc
realloc
void *realloc(void *ptr, size_t size) {
if (!ptr) {
return malloc(size);
}
struct block_meta* block_ptr = get_block_ptr(ptr);
if (block_ptr->size >= size) {
return ptr;
}
void *new_ptr = malloc(size);
if (!new_ptr) {
return NULL;
}
memcpy(new_ptr, ptr, block_ptr->size);
free(ptr);
return new_ptr;
}
calloc
void *calloc(size_t nelem, size_t elsize) {
size_t size = nelem * elsize;
void *ptr = malloc(size);
memset(ptr, 0, size);
return ptr;
}
Tester Allocator
clang -O0 -g -W -Wall -Wextra -shared -fPIC malloc.c -o malloc.so