1

我正在尝试创建自己的 malloc() 进行练习。我从这个线程得到了下面的代码。

typedef struct free_block {
    size_t size;
    struct free_block* next;
} free_block;

static free_block free_block_list_head = { 0, 0 };

// static const size_t overhead = sizeof(size_t);

static const size_t align_to = 16;

void* malloc(size_t size) {
    size = (size + sizeof(free_block) + (align_to - 1)) & ~ (align_to - 1);
    free_block* block = free_block_list_head.next;
    free_block** head = &(free_block_list_head.next);
    while (block != 0) {
        if (block->size >= size) {
            *head = block->next;
            return ((char*)block) + sizeof(free_block);
        }
        head = &(block->next);
        block = block->next;
    }

    block = (free_block*)sbrk(size);
    block->size = size;

    return ((char*)block) + sizeof(free_block);
}

void free(void* ptr) {
    free_block* block = (free_block*)(((char*)ptr) - sizeof(free_block ));
    block->next = free_block_list_head.next;
    free_block_list_head.next = block;
}

我对将内存块视为链表感到困惑。在我看来,我们基本上每次需要内存时都会调用 sbrk() 并检查我们之前请求的一些内存是否同时没有被释放。

但是我们无法检查属于其他进程的其他内存块,我们只能检查我们之前请求并添加到我们的链表中的内存。

如果是这种情况,这是最优的吗?这是标准 malloc() 的工作方式吗?有没有办法让我们处理堆上的所有内存?

请像我 5 岁一样解释,我很难理解这个概念。

4

1 回答 1

5

扩展进程数据段不会影响其他进程。在大多数(最近的)架构上,进程内存模型是扁平的,即每个进程都有一个virtual地址空间(2^322^64字节)。当进程请求额外的内存(页)时,virtual会为进程添加一个内存。事实上,这并不意味着发生任何物理内存分配,因为virtual内存可以映射到交换文件,或者在使用之前完全取消映射(地址已分配给进程,但没有分配实际资源)。内核负责virtual根据需要/每个资源可用性将物理地址映射到一个。

算法是做什么的?

当用户调用malloc算法试图找到可用的空块。一开始是没有的,所以算法试图扩展过程数据段。

但是,您可以看到,这free并没有释放虚拟内存(因为它不像分配它那么简单),而是将此released块添加到未使用块的列表中。

因此,当有预发布的块时,malloc尝试重用它们而不是扩展数据段。

做标准malloc的工作如上:不。您提供的示例很简单,但效率很低。有许多不同的算法可用于内存管理:小块堆(当分配数据达到一定数量时具有O(1)性能)、特定于线程的分配器(减少从多个线程访问堆的拥塞)、分配器(预先分配大块和然后使用它们(类似于上面,但更有效)和其他。

您可以尝试使用“内存堆实现”获取更多信息

于 2013-04-13T21:41:53.010 回答