自定义malloc,分段故障

问题描述 投票:0回答:1

我正在做一个自定义的malloc。我做了一个非常简单的,但现在我试图合并和分割块,以提高对 sbrk().当我尝试执行一个没有太多商城的自定义程序时,它可以完美地工作。但是当我尝试使用更多的mallocs或者例如命令 ls 在一些成功的分配后,当调用split函数时,它最终给出了一个奇怪的分段故障(core dumped)。

如果有任何帮助或提示,我将非常感激。

#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 %d\n", current->size_bytes, current->available);
        current = current->next;
    }
    if (current == NULL) {
        fprintf(stderr, "null\n" );
    } else {
        fprintf(stderr, "%zu libre %d\n", 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 %zu\n", 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, "gleich\n");
        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 valid\n");
        exit(1);
    }

    meta_data->available = 1;
    fprintf(stderr, "Free at %x: %zu bytes\n", 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 entrar\n");
        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 bytes\n", 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 %zu\n", meta_data->size_bytes - size_bytes);
    if ((meta_data->size_bytes - size_bytes) > 12288) {
        split(meta_data, size_bytes);
        fprintf(stderr,"call split\n");
    }

    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, "realloc\n");
    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;
};
c memory memory-management malloc
1个回答
2
投票

以下是对你的代码的一些评论。

  • 把指针隐藏在 typedefs 后面是让读者感到困惑的。为什么不定义 m_meta_data 作为类型定义的 struct m_meta_data 并使用 m_meta_data * 到处都是?
  • 你确定 sbrk() 是正确的定义?铸 (void *)sbrk(0) 似乎表明不是这样。sbrk() 是在 <unistd.h> 在POSIX系统上。
  • 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] = '\0';
        }
        return p;
    }
    
  • 分配的区块 malloc() 应该在64位英特尔系统上以16字节为界对齐。事实上,在64位的英特尔系统上 m_meta_data 结构在64位系统中的大小为32字节,而在32位系统中的大小为20字节。您应该调整您的 m_meta_data 对于32位系统来说,您应该检查32位系统的结构。

  • 你应该检查 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; 没有得到锁似乎是危险的。

  • 你应该检查 meta_data->availablefree()realloc() 以检测无效呼叫,防止竞技场损坏。

  • malloc()在分配失败的情况下,你会忘记释放锁。

  • 调用 fprintf 鎖定時的風險:如果 fprintf 电话 malloc,你会得到一个僵局。你可能会认为打印到 stderr 不叫 malloc() 因为 stderr 是无缓冲的,但你在冒险。

  • 当分配一个新的块时,用 sbrk(),你应该使用 sbrk(0) 由于可能已四舍五入至倍数,因此在分配后确定实际可用的面积。PAGE_SIZE.

  • 你应该分割块,如果 (meta_data->size_bytes - size_bytes) > SIZE_META_DATA. 目前的测试太宽松了。

© www.soinside.com 2019 - 2024. All rights reserved.