自定义内存映射分配器SEGFAULTs在释放后分配

时间:2014-10-10 01:14:25

标签: c memory-management segmentation-fault

所以我需要一个简单的分配器来分配(有时是归零)以及以后从映射内存池中释放的4K块。但是,在实现之后,在测试时我发现在释放一两个块后,如果我尝试分配一个块,程序将SEGFAULT

奇怪的是,当我连续释放多个街区时,似乎没有任何障碍。

从其他文件收集的一些重要定义:

#define xmattr_constant __attribute__((const))
#define xmattr_malloc   __attribute__((malloc))
#define xmattr_pure __attribute__((pure))

#define xm_likely(x)    __builtin_expect(!!(x), 1)
#define xm_unlikely(x)  __builtin_expect(!!(x), 0)

#define ABLKLEN 4096    // 4K pagesize

typedef struct {
    uint8_t     magic[16];  // "sfDB5" "vX.XXXXXXX" '\0'
    uint8_t     *freelist;
    uint64_t    size;
    uint64_t    bounds;
} arenaheader;

分配代码:

void *pd_arena;

void pd_init (size_t len, uint8_t *map) {
    int x;
    size_t const block = len / 256;         // arena physical size
    size_t const size = (block / ABLKLEN) * ABLKLEN;    // arena useable size
    arenaheader *header;

    for (x = 0; x < 256; x++) {
        header = (void *) &(map[x * block]);
        header->freelist = NULL;        // no free blocks because all are free
        header->size    = size;     // useable size
        header->bounds  = ABLKLEN;  // current bounds
    }

    return;
}

xmattr_malloc void *pd_mallocBK (void) {
    arenaheader *header = pd_arena;
    uint8_t *ptr;

    if (xm_unlikely (header->freelist)) {   // there's a sitting free block
        ptr = header->freelist; // return the free block
        void **next = ptr;
        header->freelist = *next;   // update the free list
    } else if (xm_likely (header->bounds < header->size)) { // no free blocks
        ptr = pd_arena;
        ptr += header->size;
        header->size += ABLKLEN;
    } else {    // no more blocks
        ptr = NULL;
    }

    return ptr;
}


xmattr_malloc void *pd_callocBK (void) {
    void *ptr = pd_mallocBK ();

    if (xm_likely (ptr))    // allocation was successful
        memset (ptr, 0, ABLKLEN);

    return ptr;
}

void pd_freeBK (void *ptr) {
    arenaheader *header = pd_arena;

    if (xm_likely (ptr)) {  // non-NULL ptr
        void *next = header->freelist;  // get current top of stack
        void **this = ptr;
        *this = next;   // move address of current top of stack to ptr
        header->freelist = ptr; // push ptr to stack
    }

    return;
}

测试代码:

#define F_LEN   (1024 * 1024 * 1024)    // 1 GB
#define A_LEN   (F_LEN / 256)

int main (int argc, char **argv) {
    int x, y;

    // setup
    int fd;
    uint8_t *map;
    assert (fd = open ("./pd_single.testout", O_CREAT | O_RDWR | O_EXCL));
    if (ftruncate (fd, F_LEN)) {
        perror ("ftruncate failed: ");
        return 1;
    }
    assert (map = mmap (NULL, F_LEN, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0));

    uint8_t *arena[256];
    for (x = 0; x < 256; x++)
        arena[x] = map + (x * A_LEN);

    // test
    volatile int *var;
    void *list[512];
    int lcnt = 0;

    pd_init (F_LEN, map);

    // per arena test
    for (x = 0; x < 256; x++) {
        pd_arena = arena[x];
        // allocate and write a few times
        for (y = 0; y < 256; y++) {
            assert ((list[lcnt] = pd_mallocBK ()));
            var = list[lcnt];
            *var = (x + 1) * (y + 1);
        }
        // free some but not all
        for (y = 0; y < 64; y++)
            pd_freeBK (list[lcnt]);

        // now reallocate some and write some
        for (y = 0; y < 16; y++) {
            assert ((list[lcnt] = pd_mallocBK()));
            var = list[lcnt];
            *var = 16;
        }
    }

    // cleanup
    munmap (map, F_LEN);
    close (fd);

    return 0;
}

通过gdb运行程序后,我发现它SEGFAULT位于pd_mallocBK()之内;特别是,在这一行:

header->freelist = *next;   // update the free list

但是,我似乎无法理解该行的错误和/或如何修复它。

所以,两个问题,真的(按重要性顺序,最多到最少):

  1. 所选行有什么问题,我该如何解决?
  2. 是否还有其他分配器,我可以简单地分配一个映射内存区域而不必实现它?

1 个答案:

答案 0 :(得分:1)

以下代码比原始代码效果更好,但在开始在最后一个舞台上工作时最终仍会崩溃。

#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#define xmattr_malloc   __attribute__((malloc))

#define xm_likely(x)    __builtin_expect(!!(x), 1)
#define xm_unlikely(x)  __builtin_expect(!!(x), 0)

enum { ABLKLEN = 4096 };

void pd_freeBK(void *ptr);
xmattr_malloc void *pd_callocBK(void);
xmattr_malloc void *pd_mallocBK(void);
void pd_init(size_t len, uint8_t *map);

typedef struct {
    uint8_t     magic[16];  // "sfDB5" "vX.XXXXXXX" '\0'
    uint8_t     *freelist;
    uint64_t    size;
    uint64_t    bounds;
} arenaheader;

static void *pd_arena;

static void pd_dump_arena(FILE *fp, const char *tag, const arenaheader *arena)
{
    assert(arena != NULL);
    fprintf(fp, "Arena: 0x%.8" PRIXPTR " - %s\n", (uintptr_t)arena, tag);
    fprintf(fp, "Size: %.8" PRIu64 ", Bounds: %.8" PRIu64 ", Freelist: 0x%.8" PRIXPTR "\n",
            arena->size, arena->bounds, (uintptr_t)arena->freelist);
}

void pd_init(size_t len, uint8_t *map)
{
    size_t const block = len / 256;                   // arena physical size
    size_t const size = (block / ABLKLEN) * ABLKLEN;  // arena useable size
    arenaheader *header;

    for (int x = 0; x < 256; x++)
    {
        header = (void *) &(map[x * block]);
        header->freelist = NULL;     // no free blocks because all are free
        header->size     = size;     // useable size
        header->bounds   = ABLKLEN;  // current bounds
    }

    for (int x = 0; x < 256; x++)
    {
        char buffer[32];
        sprintf(buffer, "arena %.3d", x);
        pd_dump_arena(stdout, buffer, (arenaheader *)&map[x * block]);
    }
}

xmattr_malloc void *pd_mallocBK(void)
{
    arenaheader *header = pd_arena;
    void *ptr;

    if (xm_unlikely(header->freelist))      // there's a sitting free block
    {
        ptr = header->freelist; // return the free block
        void **next = ptr;
        header->freelist = *next;   // update the free list
    }
    else if (xm_likely(header->bounds < header->size))      // no free blocks
    {
        ptr = pd_arena;
        ptr = (uint8_t *)ptr + header->size;
        header->size += ABLKLEN;
    }
    else        // no more blocks
    {
        ptr = NULL;
    }

    return ptr;
}

xmattr_malloc void *pd_callocBK(void)
{
    void *ptr = pd_mallocBK();

    if (xm_likely(ptr))     // allocation was successful
        memset(ptr, 0, ABLKLEN);

    return ptr;
}

void pd_freeBK(void *ptr)
{
    arenaheader *header = pd_arena;

    if (xm_likely(ptr))     // non-NULL ptr
    {
        void *next = header->freelist;  // get current top of stack
        void **this = ptr;
        *this = next;   // move address of current top of stack to ptr
        header->freelist = ptr; // push ptr to stack
    }
}

enum { NUM_ARENAS = 256 };
#define F_LEN   (1024 * 1024 * 1024)    // 1 GB
#define A_LEN   (F_LEN / NUM_ARENAS)

int main(void)
{
    const char filename[] = "./pd_single.testout";
    // setup
    //int fd = open(filename, O_CREAT | O_RDWR | O_EXCL, 0444);
    int fd = open(filename, O_CREAT | O_RDWR, 0600);
    assert(fd >= 0);
    if (ftruncate(fd, F_LEN))
    {
        unlink(filename);
        perror("ftruncate failed: ");
        return 1;
    }
    uint8_t *map = mmap(NULL, F_LEN, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
    assert(map != MAP_FAILED);

    uint8_t *arena[NUM_ARENAS];
    for (int x = 0; x < NUM_ARENAS; x++)
        arena[x] = map + (x * A_LEN);

    pd_init(F_LEN, map);

    // test
    void *list[512];

    // per arena test
    for (int x = 0; x < NUM_ARENAS; x++)
    {
        int lcnt = 0;
        pd_arena = arena[x];
        printf("Arena[%.3d] = 0x%.8" PRIXPTR "\n", x, (uintptr_t)pd_arena);
        // allocate and write a few times
        for (int y = 0; y < 256; y++)
        {
            assert((list[lcnt] = pd_mallocBK()));
            int *var = list[lcnt];
            *var = (x + 1) * (y + 1);
            printf("[%.3d] data 0x%.8" PRIXPTR " = %d\n", y, (uintptr_t)list[lcnt], *var);
            lcnt++;
        }

        // free some but not all
        lcnt = 0;
        for (int y = 0; y < 64; y++)
        {
            printf("[%.3d] free 0x%.8" PRIXPTR " = %d\n", y, (uintptr_t)list[lcnt], *(int *)list[lcnt]);
            pd_freeBK(list[lcnt]);
            lcnt++;
        }

        // now reallocate some and write some
        lcnt = 0;
        for (int y = 0; y < 16; y++)
        {
            assert((list[lcnt] = pd_mallocBK()));
            int *var = list[lcnt];
            *var = 16;
            printf("[%.3d] data 0x%.8" PRIXPTR " = %d\n", y, (uintptr_t)list[lcnt], *var);
            lcnt++;
        }
    }

    // cleanup
    munmap(map, F_LEN);
    close(fd);
    unlink(filename);

    return 0;
}

我还没有找到残留的错误。请注意lcnt中的诊断打印(详细)和main()的不同处理。您忙于多次释放相同的内存,但未在pd_freeBK()代码中检测到该内存。您还在泄漏记忆,因为您没有在lcnt中增加main()