【问题标题】:Custom Memory Mapped Allocator SEGFAULTs on allocate after freeing释放后分配的自定义内存映射分配器 SEGFAULT
【发布时间】:2014-12-05 02:12:50
【问题描述】:

所以我需要一个简单的分配器来分配(有时使用归零),然后从映射内存池中释放 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运行程序后,发现SEGFAULTs在pd_mallocBK()内;具体来说,在这一行:

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

但是,我似乎无法理解该行有什么问题和/或如何解决它。

所以,有两个问题,真的(按重要性从高到低排列):

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

【问题讨论】:

  • ABLKLEN 是否设置为 4096?你不能展示一个main() 程序,它发出一系列触发故障的调用吗?你用valgrind跑了吗?了解如何创建 MCVE (Minimal, Complete, Verifiable Example) 或 SSCCE (Short, Self-Contained, Correct Example)。
  • @JonathanLeffler 添加了我使用的测试程序和ABLKLEN 的定义。
  • 几乎可以编译:我必须添加 8 个标题。 A_LENF_LEN 是什么?为什么你认为void **next = ptr; 行应该编译?它不适合我(Mac OS X 10.9.5 上的 GCC 4.9.1,带有编译器选项 gcc -g -O3 -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Werror xma.c -o xma,这是我用于编译的默认选项集)。
  • 修复了LENs的部分;我在 Ubuntu 14.04.1 上使用了带有 GCC 4.8.2 的选项 gcc -g xma.c -o xma
  • 警告不够安全! :D 我将不得不研究你认为赋值是什么,但编译器认为它是可疑的,所以这是首先要注意的事情。顺便说一句,只有一个警告(在我为您定义的各种函数提供原型之后)也不错。

标签: c memory-management segmentation-fault


【解决方案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;
}

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

【讨论】:

  • 我知道为什么它仍然在第 256 块死掉:出于某种奇怪的原因,我将竞技场的大小添加到 pd_mallocBK() 中的指针。相反,我应该完成ptr += header-&gt;bounds;header-&gt;bounds += ABLKLEN
猜你喜欢
  • 2012-07-07
  • 2011-12-09
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 2021-11-25
  • 2019-03-06
  • 1970-01-01
相关资源
最近更新 更多