我目前正在重新编码我自己的
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
问题可能来自于您的
malloc
函数与 C 库中的函数之间的不兼容:printf
函数直接或间接使用 malloc
来分配一些内存供内部使用,并且这样分配的块会被 释放。 free
。您替换了 malloc
,因此 printf
和/或 FILE
处理函数使用您的 malloc
,但 free
函数无法处理由 malloc
函数分配的块,这会导致未定义的行为和分段错误.
您应该为您的
malloc
克隆使用不同的名称并实现所有分配函数:malloc
、calloc
、realloc
、aligned_alloc
free
、strdup
...以及可能的其他一些函数,并在更改名称以使用标准名称之前彻底测试它们。