C - 自定义 malloc,分段错误



我正在做一个自定义malloc。我做了一个非常简单的,但现在我正在尝试合并和拆分块以提高调用sbrk()的效率。当我尝试执行没有很多mallocs的自定义程序时,它可以完美运行。但是,一旦我尝试更多的mallocs或例如命令在一些成功的分配后ls,它就会在调用split函数时给出一个奇怪的分段错误(核心转储(。

任何帮助或提示将不胜感激。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "struct.h"
static p_meta_data first_element = NULL;
static p_meta_data last_element  = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define ALIGN8(x) (((((x)-1)>>3)<<3)+8)
#define MAGIC     0x87654321
void *malloc(size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes);
p_meta_data request_space(size_t size_bytes);
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2);
void split(p_meta_data meta_data, size_t size_bytes);
void free(void *ptr);
void *calloc(size_t num_bytes, size_t num_blocs);
void *realloc(void *ptr, size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes) {
p_meta_data current = first_element; 
while (current && !(current->available && current->size_bytes >= size_bytes)){
fprintf(stderr, " %zu libre %dn", current->size_bytes, current->available);
current = current->next;
}
if (current == NULL) {
fprintf(stderr, "nulln" );
} else {
fprintf(stderr, "%zu libre %dn", current->size_bytes, current->available);
}
return current;
}
p_meta_data request_space(size_t size_bytes) {
if (size_bytes < 122880) {
size_bytes = 122880;
fprintf(stderr, "request %zun", size_bytes);
}
p_meta_data meta_data;
meta_data = (void *)sbrk(0);
if (sbrk(SIZE_META_DATA + size_bytes) == (void *)-1)
return (NULL);
meta_data->size_bytes = size_bytes;
meta_data->available = 0;
meta_data->magic = MAGIC;
meta_data->next = NULL;
meta_data->previous = NULL;
return meta_data;
}
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2) {
if (!meta_data1 || !meta_data2) {
return NULL;
}
meta_data1->size_bytes = meta_data1->size_bytes + SIZE_META_DATA + meta_data2->size_bytes;
meta_data1->next = meta_data2->next;
if (last_element == meta_data2) {
fprintf(stderr, "gleichn");
last_element = meta_data1;
}
meta_data2 = NULL;
return meta_data1;
}
void free(void *ptr) {
p_meta_data meta_data;
if (!ptr)
return;
pthread_mutex_lock(&mutex);
meta_data = (p_meta_data)(ptr - SIZE_META_DATA);
if (meta_data->magic != MAGIC) {
fprintf(stderr, "ERROR free: value of magic not validn");
exit(1);
}
meta_data->available = 1;
fprintf(stderr, "Free at %x: %zu bytesn", meta_data, meta_data->size_bytes);
p_meta_data meta_data_prev, meta_data_next;
meta_data_prev = meta_data->previous;
meta_data_next = meta_data->next;
if (meta_data_prev && meta_data_prev->available) {
meta_data = merge(meta_data_prev, meta_data);
}
if (meta_data_next && meta_data_next->available) {
meta_data = merge(meta_data, meta_data_next);
}
pthread_mutex_unlock(&mutex);
}
void split(p_meta_data meta_data, size_t size_bytes) {
if (!meta_data) {
fprintf(stderr, "no deberia entrarn");
return;
}
p_meta_data meta_data2;
size_t offset = SIZE_META_DATA + size_bytes;
meta_data2 = (p_meta_data)(meta_data + offset);
fprintf(stderr, "size of metadata %d", meta_data->size_bytes - size_bytes - SIZE_META_DATA);
meta_data2->size_bytes = meta_data->size_bytes - size_bytes - SIZE_META_DATA;
meta_data2->available = 1;
meta_data2->magic = MAGIC;
meta_data2->previous = meta_data;
meta_data2->next = meta_data->next;
if (meta_data == last_element) {
last_element = meta_data2;
}
meta_data->size_bytes = size_bytes;
meta_data->next = meta_data2;
return;
}
void *malloc(size_t size_bytes) {
void *p, *ptr;
p_meta_data meta_data;
if (size_bytes <= 0) {
return NULL;
}
size_bytes = ALIGN8(size_bytes);
fprintf(stderr, "Malloc %zu bytesn", size_bytes);
// Bloquegem perque nomes hi pugui entrar un fil
pthread_mutex_lock(&mutex);
meta_data = search_available_space(size_bytes);
if (meta_data) { // free block found
fprintf(stderr, "FREE BLOCK FOUND---------------------------------------------------n");
meta_data->available = 0; //reservamos el bloque
} else {     // no free block found
meta_data = request_space(size_bytes); //pedimos más espacio del sistema
if (!meta_data) //si meta_data es NULL (es decir, sbrk ha fallado)
return (NULL);
if (last_element) // we add the new block after the last element of the list
last_element->next = meta_data;
meta_data->previous = last_element;
last_element = meta_data;
if (first_element == NULL) // Is this the first element ?
first_element = meta_data;
}
fprintf(stderr, "die differenz %zun", meta_data->size_bytes - size_bytes);
if ((meta_data->size_bytes - size_bytes) > 12288) {
split(meta_data, size_bytes);
fprintf(stderr,"call splitn");
}
p = (void *)meta_data;
// Desbloquegem aqui perque altres fils puguin entrar
// a la funcio
pthread_mutex_unlock(&mutex);
// Retornem a l'usuari l'espai que podra fer servir.
ptr = p + SIZE_META_DATA; //p es puntero al inicio de meta_data, y ptr es el puntero al inicio del bloque de datos en sí (justo después de los metadatos)
return ptr;
}
void *calloc(size_t num_bytes, size_t num_blocs) {
size_t mem_to_get = num_bytes * num_blocs;
void *ptr = malloc(mem_to_get);
if (ptr == NULL) {
return ptr;
} else {
memset(ptr, 0, mem_to_get);
return ptr;
}
}
void *realloc(void *ptr, size_t size_bytes) {
fprintf(stderr, "reallocn");
if (ptr == NULL) {
return malloc(size_bytes);
} else {
p_meta_data inic_bloc = (p_meta_data )(ptr - SIZE_META_DATA);
if (inic_bloc->size_bytes >= size_bytes) {
return ptr;
} else {
void *new_p = malloc(size_bytes);
memcpy(new_p, ptr, inic_bloc->size_bytes);
inic_bloc->available = 1;
return new_p;
}
}
}

其中 struct.h 是:

#include <stddef.h>
#include <unistd.h>
#define SIZE_META_DATA  sizeof(struct m_meta_data)
typedef struct m_meta_data *p_meta_data;
/* This structure has a size multiple of 8 */
struct m_meta_data {
size_t  size_bytes;
int     available;
int     magic;
p_meta_data next;
p_meta_data previous;
};

以下是有关代码的一些备注:

  • 对于读者来说,将指针隐藏在 Typedef 后面是令人困惑的。为什么不将m_meta_data定义为struct m_meta_data的 typedef 并在任何地方使用m_meta_data *
  • 您确定sbrk()定义正确吗?演员表(void *)sbrk(0)似乎表明并非如此。sbrk()在 POSIX 系统上以<unistd.h>形式声明。
  • BUGsplit(),计算meta_data2 = (p_meta_data)(meta_data + offset);不正确。它应该是:

    meta_data2 = (p_meta_data)((unsigned char *)meta_data + offset);
    
  • 您应该定义strdup()strndup(),因为 C 库中的定义可能不会调用重新定义的malloc()

    char *strdup(const char *s) {
    size_t len = strlen(s);
    char *p = malloc(len + 1);
    if (p) {
    memcpy(p, s, len + 1);
    }
    return p;
    }
    char *strndup(const char *s, size_t n) {
    size_t len;
    char *p;
    for (len = 0; len < n && s[n]; len++)
    continue;
    if ((p = malloc(len + 1)) != NULL) {
    memcpy(p, s, len);
    p[len] = '';
    }
    return p;
    }
    
  • 分配了malloc()的块应在 64 位英特尔系统上的 16 字节边界上对齐。事实上,m_meta_data结构在 64 位系统上的大小为 32 字节,但在 32 位系统上的大小为 20 字节。您应该调整 32 位系统的m_meta_data结构。

  • 您应该检查size_t mem_to_get = num_bytes * num_blocs;中的溢出
  • 你不应该依赖void *算术,它是一个GCC扩展。改为写这个:

    p_meta_data inic_bloc = (p_meta_data)ptr - 1;
    
  • realloc()中,当扩展块的大小时,你只是使原始块可用,但你不会像在free()中那样将其与相邻的块合并。您可能只是调用free(ptr),特别是因为修改inic_bloc->available = 1;而不获得锁似乎有风险。

  • 您应该在free()realloc()中检查meta_data->available,以检测无效呼叫并防止竞技场损坏。

  • malloc()中,您忘记在分配失败时释放锁。

  • 在设置锁时调用fprintf是有风险的:如果fprintf调用malloc,你会得到死锁。您可能会认为打印到stderr不会调用malloc(),因为stderr未缓冲,但您正在冒险。

  • 当分配带有sbrk()的新块时,您应该在分配后使用sbrk(0)来确定可用的实际大小,因为它可能已四舍五入为PAGE_SIZE的倍数。

  • 如果(meta_data->size_bytes - size_bytes) > SIZE_META_DATA,您应该拆分块。目前的测试太松散了。

相关内容

  • 没有找到相关文章

最新更新