【问题标题】:Defragment Memory Blocks in C Memory Pool对 C 内存池中的内存块进行碎片整理
【发布时间】:2016-04-14 19:58:25
【问题描述】:

我已经用 C 语言构建了一个简单的内存池,并且我还实现了在这个池中实现内存块的能力。

内存块本身非常简单,只是一个带有空闲标志和大小属性的双向链表。

我现在要做的是创建一个函数,该函数接受指向我的内存池的指针并对内部的内存块进行碎片整理,以便分配的(空闲 == 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 编译器。

【问题讨论】:

  • 已分配块的所有者在重新定位后会做什么?
  • @WeatherVane 目前,我不需要拥有所有者的指针,因为这是为 uni 分配的。虽然有一个分配的中间目录并让指针保持不变会给我带来更高的分数,但这不是低分数的要求。
  • 碎片整理程序不知道用户将内存指针放在哪里,因此您无法更新它们。当然,您只能对未使用的区域进行碎片整理。就像对硬盘进行碎片整理时一样,打开的文件无法移动。
  • 对不起,那太荒谬了 (@Liam)。内存池中没有任何意义,它的内存可能会从您的下方移出。

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


【解决方案1】:

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

if (newblock->next)
    new_block->next->prev = new_block;

在上面的else 部分。

其他问题

  • 如果您的块不是立即连续且在池中按顺序排列,这将导致行为不端。您可能会确保整个池是单个连续的内存块,但如果其他一些例程对事物进行重新排序,您可能仍然会遇到问题。对于偏执型编程,坚持断言以确保不违反这些不变量是一个好主意。
  • 任何指向池的外部指针在此操作后都将成为垃圾
  • 如果您有奇数大小的块(您看起来是这样),您将获得未对齐的指针,这充其量是低效的,最坏的情况可能会崩溃。
  • 对于像您所描述的池而言,具有包含指向池的指针的固定大小“句柄”的关联数组是正常的,并且每当您移动非空闲块时,您都需要更新指针对应的手柄。手柄也可能具有禁止移动块的“锁定”标志 - 在这种情况下,您需要检查该标志并仅移动未锁定的块,并且当您移动块时,您可能希望移动到不相邻的块中。这反过来可能会让您在上述第一点上遇到麻烦。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-13
    • 2011-07-25
    • 1970-01-01
    • 2012-05-13
    • 2013-08-14
    • 2021-01-07
    • 2010-10-26
    • 2013-05-11
    相关资源
    最近更新 更多