C 中分割内存块时出现分段错误

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

我目前正在重新编码我自己的

malloc()
版本,我目前仅使用
sbrk()
,并且希望稍后使用
mmap()
实现一个版本。

这是块元数据结构:

typedef struct MV_BLOCK_S {
    uint32_t magic;
    struct MV_BLOCK_S *prev;
    uint64_t size;
    uint8_t state; // First bit: 0 = block free, 1 = block used, second bit: 0 = sbrk() 1 = mmap()
    void *data;
    struct MV_BLOCK_S *next;
} mv_block_t;

我使用的一些宏:

#define MV_ALIGN(x) (((((x) - 1) >> 3) << 3) + 8)
#define MV_BLOCK_T_SIZE  MV_ALIGN(sizeof(mv_block_t))
#define MV_BLOCK_T_END_OFFSET MV_BLOCK_T_SIZE - sizeof(mv_block_t)
#define MV_MAGIC 213213213
#define MV_MMAP_THRESHOLD 4096 // WIP if size > 4096, use mmap()
#define MV_FREE_BIT(x) (x & 0x01)
#define MV_ALLOC_BIT(x) ((x >> 1) & 0x01)

目前只实现了

malloc()
功能:

static void *mv_heap = NULL;
static mv_block_t *end_heap = NULL;
static mv_block_t *last_block = NULL;
static size_t last_size = 0;

static void *mv_search_block(size_t size)
{
    mv_block_t *block = NULL;
    if (last_block == NULL && last_size < size) {
        block = mv_heap;
    } else {
        block = last_block;
    }
    while (block != NULL) {
        if (block->size >= size && MV_FREE_BIT(block->state) == 0)
            break;
        block = block->next;
    }
    if (block != NULL) {
        if (mv_split_block(block, size) == 0) {
            end_heap = block->next;
        }
        block->state = 0x02;
    } else {
        if (sbrk(size) == (void *)-1)
            return (NULL);
        block = sbrk(0);
        block->magic = MV_MAGIC;
        block->prev = end_heap;
        block->size = size - MV_BLOCK_T_SIZE;
        block->state = 0x02;
        block->data = (unsigned char *)block + MV_BLOCK_T_SIZE;
        block->next = NULL;
        end_heap = block;
    }
    last_block = block;
    last_size = size;
    return (block->data);
}

static void *init_heap_sbrk(size_t size)
{
    mv_heap = sbrk(size);
    if (mv_heap == (void *)-1)
        return (NULL);
    mv_block_t *block = mv_heap;
    block->magic = MV_MAGIC;
    block->prev = NULL;
    block->size = size - MV_BLOCK_T_SIZE;
    block->state = 0x01;
    block->data = (unsigned char *)mv_heap + MV_BLOCK_T_SIZE;
    block->next = NULL;
    end_heap = block;
    return (block->data);
}

void *malloc(size_t size)
{
    if (size == 0)
        size = MV_BLOCK_T_SIZE;
    else
        size = MV_ALIGN(size) + MV_BLOCK_T_SIZE;
    if (size >= PTRDIFF_MAX)
        return (NULL);
    if (mv_heap == NULL)
        return init_heap_sbrk(size);
    return (mv_search_block(size));
}

管理内存碎片(合并/块释放/拆分):

int mv_merge_block(mv_block_t *current)
{
    mv_block_t *prev = current->prev;
    mv_block_t *next = current->next;

    if (prev != NULL && MV_FREE_BIT(prev->state) == 0) {
        prev->size += current->size + MV_BLOCK_T_SIZE;
        prev->next = next;
        current = prev;
    }
    if (next != NULL && MV_FREE_BIT(next->state) == 0) {
        if (next->next != NULL) {
            current->size += next->size + MV_BLOCK_T_SIZE;
            current->next = next->next;
        } else {
            current->next = NULL;
            if (sbrk(-(next->size + MV_BLOCK_T_SIZE)) == (void *)-1)
                return (-1);
        }
    }
    return (0);
}

int mv_suppress_block(mv_block_t *block)
{
    if (block == NULL)
        return (-1);
    if (MV_FREE_BIT(block->state) == 0 && block->next == NULL) {
        if (block->prev != NULL) {
            block->prev->next = NULL;
        }
        if (sbrk(-(block->size + MV_BLOCK_T_SIZE)) == (void *)-1)
            return (-1);
        return (0);
    }
    return (-1);
}

int mv_split_block(mv_block_t *block, size_t size)
{
    size_t new_block_size = block->size - size;
    mv_block_t *new_block = NULL;

    if (new_block_size < MV_BLOCK_T_SIZE)
        return (-1);
    if (mv_suppress_block(block->next) == 0) {
        return (-1);
    }
    new_block = (mv_block_t *)(unsigned char *)block + size;
    new_block->magic = MV_MAGIC;
    new_block->prev = block;
    new_block->size = new_block_size - MV_BLOCK_T_SIZE;
    new_block->state = 0x00;
    new_block->data = (unsigned char *)new_block + MV_BLOCK_T_SIZE;
    new_block->next = block->next;
    block->size = size;
    block->next = new_block;
    return (0);
}

为了能够查看我的块,我编写了应该显示有用信息的函数:

void mv_dump_block(mv_block_t *block)
{
    uint8_t usage_bit = block->state & 0x01;
    uint8_t allocation_method_bit = (block->state >> 1) & 0x01;

    printf("========================================\n");
    printf("Block : %p\n", (void *)block);
    printf("Magic : %d\n", block->magic);
    printf("Prev : %p\n", (void *)block->prev);
    printf("Size : %ld\n", block->size);
    printf("State of block : %s\n", (usage_bit == 0) ? "Available" : "Busy");
    printf("Allocation's type : %s\n", (allocation_method_bit == 0) ? "sbrk" : "mmap");
    printf("Data : %p\n", block->data);
    printf("Next : %p\n", (void *)block->next);
    printf("========================================\n");
}

void mv_dump_heap(void *ptr)
{
    mv_block_t *block = mv_get_block(ptr);

    while (block != NULL && block->prev != NULL)
        block = block->prev;
    while (block != NULL) {
        mv_dump_block(block);
        block = block->next;
    }
}

mv_block_t *mv_get_block(void *ptr)
{
    mv_block_t *block = (mv_block_t *)((unsigned char *)ptr - MV_BLOCK_T_SIZE);

    if (block->magic != MV_MAGIC)
        return (NULL);
    return (block);
}

这是我的

main()
测试功能:

int main(void)
{
    char *ptr = malloc(10);
    char *ptr2 = malloc(4096);
    mv_dump_heap(ptr);
    (void)ptr2;
    return (0);
}

这是 gdb 报告:

Program received signal SIGSEGV, Segmentation fault.
0x00005555555557f2 in mv_split_block (block=0x55555555a070, size=1072) at src/fragmentation.c:52
52          new_block->magic = MV_MAGIC;

目前,我正在使用 GLIBC 和以下标志进行编译:

-m64 -fPIC -pedantic -Wall -Wextra -Werror -ggdb3

你能帮我解决这个问题吗?我猜

new_block
变量在分割过程中未对齐。

更新

由于我对

malloc(0)
的测试在评论中显然令人不安,因此我将其替换为 10 并得到相同的结果。对于那些误读的人,我目前仅实施了
malloc()

我还添加了我的 Makefile :

CC          =       gcc
CFLAGS      =       -m64 -fPIC -pedantic -Wall -Wextra -Werror -ggdb3
LFLAGS      =       -I./include

SRC         =       $(shell find src/ -type f -name '*.c')
OBJ         =       $(SRC:.c=.o)

NAME        =       test/vlibc_malloc_test

all: $(NAME)

$(NAME): $(OBJ)
    @$(CC) $(CFLAGS) $(OBJ) -o $(NAME) $(LFLAGS)

%.o: %.c
    @$(CC) $(CFLAGS) $(LFLAGS) -c $< -o $@

clean:
    @rm -f $(OBJ)

fclean: clean
    @rm -f $(NAME)

re: fclean all

.PHONY: clean fclean re

可重现的代码集

完整代码在这里:在线GDB

c malloc heap-memory
1个回答
0
投票

问题可能来自于您的

malloc
函数与 C 库中的函数之间的不兼容:
printf
函数直接或间接使用
malloc
来分配一些内存供内部使用,并且这样分配的块会被
 释放。 free
。您替换了
malloc
,因此
printf
和/或
FILE
处理函数使用您的
malloc
,但
free
函数无法处理由
malloc
函数分配的块,这会导致未定义的行为和分段错误.

您应该为您的

malloc
克隆使用不同的名称并实现所有分配函数:
malloc
calloc
realloc
aligned_alloc
free
strdup
...以及可能的其他一些函数,并在更改名称以使用标准名称之前彻底测试它们。

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