C内存池中的内存块碎片整理

时间:2016-04-14 19:58:25

标签: c memory memory-management heap-memory defragmentation

我在C中构建了一个简单的内存池,我也实现了在这个池中实现内存块的能力。

内存块本身非常简单,只是一个带有free flag和size属性的双向链表。

我现在要做的是创建一个函数,该函数接收指向我的内存池的指针并对内部的内存块进行碎片整理,以便分配的(free == 0)块位于池的开头,并且释放的块是在游泳池尽头。

例如,如果在调用我的碎片整理功能之前,我有像这样构造的内存块:

Block Size: 25 (41 w/ header), Free: 1
Block Size: 100 (116 w/ header), Free: 0
Block Size: 25 (41 w/ header), Free: 1
Block Size: 100 (116 w/ header), Free: 0
Block Size: 100 (116 w/ header), Free: 0
Block Size: 54 (70 w/ header), Free: 1

然后在调用我的函数后,块将按如下方式排列:

Block Size: 100 (116 w/ header), Free: 0
Block Size: 100 (116 w/ header), Free: 0
Block Size: 100 (116 w/ header), Free: 0
Block Size: 25 (41 w/ header), Free: 1
Block Size: 25 (41 w/ header), Free: 1
Block Size: 54 (70 w/ header), Free: 1

我已经尝试构建该函数但是我遇到了一个问题,在它周围移动正确的块似乎是在调用该函数后这是我的输出:

Block Size: 100 (116 w/ header), Free: 0
Block Size: 25 (41 w/ header), Free: 1
Block Size: 100 (116 w/ header), Free: 0
Block Size: 1744830464 (1744830480 w/ header), Free: 21

我不确定该功能在哪里执行不正确的操作,所以希望有人可以为我阐明这一点。

我的碎片整理功能:

void defragment(Pool* pool)
{
    if(pool && pool->root)
    {
        Block* current = pool->root;

        while(current)
        {
            if(!current->free)
            {
                Block* current_prev = current->prev;

                if(current_prev && current_prev->free)
                {
                    Block* prev_prev = current_prev->prev;
                    int new_block_size = current_prev->size;

                    Block* moved_current = memmove(current_prev, current, sizeof(Block) + current->size);

                    if(!moved_current)
                    {
                        printf("couldn't move memory\n");
                    }
                    else
                    {
                        Block* new_block = initBlock((((char*)moved_current) + sizeof(Block) + moved_current->size), new_block_size);
                        new_block->prev = moved_current;
                        new_block->next = moved_current->next;

                        moved_current->prev = prev_prev;
                        moved_current->next = new_block;

                        if(prev_prev)
                        {
                            prev_prev->next = moved_current;
                        }

                        current = moved_current;
                        continue;
                    }
                }
            }

            current = current->next;
        }

        //Join Contiguous Blocks
    }
}

对initBlock函数的调用只需要一个内存地址,将其视为一个Block结构,然后将free属性设置为true,将size属性设置为给定的大小。

我正在使用带有-std = C99标志的GCC编译器。

1 个答案:

答案 0 :(得分:1)

在交换一对块之后,您似乎没有更新下一个块的prev字段。因此,当您前进到下一个块并检查前一个块是否空闲时,您将访问垃圾。你需要像

这样的东西
if (newblock->next)
    new_block->next->prev = new_block;

在上面的else部分。

其他问题

  • 如果你的积木没有立即连续并且在游泳池内按顺序排列,那么这将是一个错误的行为。您可能确保整个池是一个连续的记忆块,但如果其他常规重新排序,您可能仍会遇到问题。对于偏执编程,最好坚持使用断言来确保不违反这些不变量。
  • 在此操作之后,任何进入池的外部指针都将是垃圾
  • 如果您有奇数大小的块(您看起来像),您将得到未对齐的指针,这些指针最多效率低,而且最糟糕的可能会崩溃。
  • 像你所描述的那样一个池是正常的,它有一个固定大小的“句柄”数组,其中包含指向池的指针,每当你移动一个非空闲块时,你需要更新指针。相应的句柄。句柄也可能具有禁止移动块的“锁定”标志 - 在这种情况下,您需要检查该标志并仅移动未锁定的块,并且当您移动块时,您可能希望移动到非相邻块。这可能反过来让您在上面的第一点遇到麻烦。