Segmentation Fault

コアダンプの数だけ強くなれるよ、デスマと戦うエンジニアのように。

C言語で簡易メモリ管理

Linux Kernelのリスト構造を参考に作成。

環境:CentOS 6, gcc version 4.4.7

mempool.h

#ifndef _MEMPOOL_H_
#define _MEMPOOL_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <stddef.h>

#define EASY_ELECTORIC_FENCE (0x123456789)

#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define GET_ENTRY(ptr,type,member) \
    ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

#define Malloc(size) \
    __Malloc(size, __FILE__, __LINE__)

#define Free(ptr) \
    __Free(ptr)

struct mem_list {
    struct mem_list *prev;
    struct mem_list *next;
};

typedef struct {
    struct mem_list head;
    int32_t         entry_count;
} MemListHead_t;

typedef struct {
    struct mem_list list;
    int64_t         fence;      /* electoric fence */
    char            file[64];   /* ファイル名 */
    int32_t         line;       /* 行数 */
    struct timeval  tv;         /* 取得時刻 */
    int32_t         size;       /* memory size */
    void*           address;    /* dataのアドレス */
    uint8_t         data[0];    /* ユーザに渡すポインタ */
} MemListEntry_t;

static inline memlist_init(
    struct mem_list *head)
{
    head->prev = head;
    head->next = head;
}

static inline memlist_add_tail(
    struct mem_list *entry,
    struct mem_list *head)
{
    head->prev->next = entry;
    entry->next = head;
    entry->prev = head->prev;
    head->prev = entry;
}

static inline memlist_del(
    struct mem_list *entry,
    struct mem_list *head)
{
    entry->prev->next = entry->next;
    entry->next->prev = entry->prev;
    entry->next = (struct mem_list *)0xdeaddead;
    entry->prev = (struct mem_list *)0xdeaddead;
}

extern void* __Malloc(size_t size, char*  file, size_t line);
extern void __Free(void* ptr);

#endif /* _MEMPOOL_H_ */

mempool.c

#include "mempool.h"

extern MemListHead_t MngMem;

void* __Malloc(
    size_t size,
    char*  file,
    size_t line)
{
    void* ptr = NULL;
    MemListEntry_t* entry =
        (MemListEntry_t*) malloc(sizeof(*entry)+size);

    if (NULL != entry) {

        entry->fence = EASY_ELECTORIC_FENCE;
        entry->size  = size;
        snprintf(entry->file, sizeof(entry->file)-1, "%s", file);
        entry->line = line;
        gettimeofday(&entry->tv, NULL);
        entry->address = &entry->data[0];

        memlist_add_tail(&entry->list, &MngMem.head);

        ptr = &entry->data[0];
    }

    return ptr;
}

void __Free(
    void* ptr)
{
    MemListEntry_t* entry =
        (MemListEntry_t*)((uint8_t*)ptr - sizeof(MemListEntry_t));

    if(!(entry->address == ptr && EASY_ELECTORIC_FENCE != entry->fence)) {
        /* memory currupted or invalid address */
    }

    memlist_del(&entry->list, &MngMem.head);
    free(entry);
}


memtest.c (動作確認用のソースコード)

#include "mempool.h"

MemListHead_t MngMem;

typedef struct {
    int a;
    int b;
    int c;
    int d;
    int e[1024];
} testData_t;

int main(int argc, char *argv[])
{
    /* メモリ管理リスト初期化 */
    memlist_init(&MngMem.head);

    /* メモリ領域を確保 */
    testData_t *dat1 =
        (testData_t* )Malloc(sizeof(*dat1));
    testData_t *dat2 =
        (testData_t* )Malloc(sizeof(*dat2)+1);
    testData_t *dat3 =
        (testData_t* )Malloc(sizeof(*dat3)+2);

   
    /* dat1,2,3がメモリ管理リストに追加されているか確認 */
    struct mem_list *p;
    printf("##### BEFORE #####\n");
    printf("file:line, size, fence, address\n");
    printf("-------------------------------\n");
    list_for_each(p, &(MngMem.head)) {
        MemListEntry_t *gp = GET_ENTRY(p, MemListEntry_t, list);
        printf("%s:%u, %d, %#llx, %p\n",
               gp->file, gp->line, gp->size, gp->fence, gp->address);
    }

    /* dat2の削除 */
    Free(dat2);

    /* メモリ管理リストからdat2のエントリが消えているか確認 */
    printf("##### AFTER1 #####\n");
    printf("file:line, size, fence, address\n");
    printf("-------------------------------\n");
    list_for_each(p, &(MngMem.head)) {
        MemListEntry_t *gp = GET_ENTRY(p, MemListEntry_t, list);
        printf("%s:%u, %d, %#llx, %p\n",
               gp->file, gp->line, gp->size, gp->fence, gp->address);
    }

    /* dat1, dat3の削除 */
    Free(dat1);
    Free(dat3);
    
    /* メモリ管理リストからエントリがすべて消えているか確認 */
    printf("##### AFTER2 #####\n");
    printf("file:line, size, fence, address\n");
    printf("-------------------------------\n");
    list_for_each(p, &(MngMem.head)) {
        MemListEntry_t *gp = GET_ENTRY(p, MemListEntry_t, list);
        printf("%s:%u, %d, %#llx, %p\n",
               gp->file, gp->line, gp->size, gp->fence, gp->address);
    }

    return 0;
}


Makefile

memtest: memtest.o mempool.o
        gcc -g -o memtest memtest.o mempool.o

memtest.o: memtest.c mempool.h
        gcc -g -c memtest.c

mempool.o: mempool.c mempool.h
        gcc -g -c mempool.c

clean: *.o
        rm -f *.o


動作確認

$ make
gcc -g -c memtest.c
gcc -g -c mempool.c
gcc -g -o memtest memtest.o mempool.o
$
$ ./memtest
##### BEFORE #####
file:line, size, fence, address
-------------------------------
memtest.c:18, 4112, 0x123456789, 0x6b7090
memtest.c:20, 4113, 0x123456789, 0x6b8130
memtest.c:22, 4114, 0x123456789, 0x6b91d0
##### AFTER1 #####
file:line, size, fence, address
-------------------------------
memtest.c:18, 4112, 0x123456789, 0x6b7090
memtest.c:22, 4114, 0x123456789, 0x6b91d0
##### AFTER2 #####
file:line, size, fence, address
-------------------------------