📜  二次幂的自由列表分配器|核心记忆分配器(1)

📅  最后修改于: 2023-12-03 15:36:03.634000             🧑  作者: Mango

二次幂的自由列表分配器和核心记忆分配器

介绍

程序员在编写代码时,经常需要用到内存分配器。内存分配器是一种用于管理内存的API,它允许程序员请求内存来存储数据结构,并在使用后释放该内存。因为内存分配和释放是一种常见的操作,所以内存分配器应该是高效的、可重复使用的和可靠的。

二次幂的自由列表分配器和核心记忆分配器都是常见的内存分配器之一。本文将对这两种内存分配器进行介绍。

二次幂的自由列表分配器

二次幂的自由列表分配器是一种实现动态内存分配的算法。它的原理是根据用户需求的大小分配最接近的空闲内存块。该算法通常包含两个步骤:

  • 对于用户请求的大小,在已分配的块中找到最接近的块。
  • 如果找到的块已经被分割成两个较小的块,请将其中一个块分配给用户并将剩余部分添加到空闲列表中。

该算法仅适用于二次幂大小的块,因为它会将一个块拆分成两个大小相等的块。由于二次幂的幂次是以2为底的对数,因此可以保证在分割块时块大小符合这个规则。

以下是一个简单的实现:

#include <stdio.h>
#include <math.h>

#define MAX_ORDER (20)
#define BLOCK_SIZE (1024)

struct free_item {
  struct free_item* next;
};

struct zone {
  int order;
  int free_size;
  struct free_item* free_list;
  char* start_addr;
};

struct zone_allocator {
  struct zone zones[MAX_ORDER];
};

static struct zone_allocator G_alloc;

void* alloc_zone(int order) {
  struct zone* z = &G_alloc.zones[order];
  if (z->free_list) {
    struct free_item* item = z->free_list;
    z->free_list = item->next;
    z->free_size -= BLOCK_SIZE << order;
    return item;
  }
  if (order + 1 < MAX_ORDER && G_alloc.zones[order + 1].free_list) {
    void* ptr = alloc_zone(order + 1);
    split_zone(ptr, order);
    return alloc_zone(order);
  }
  return NULL;
}

void free_zone(void* ptr, int order) {
  struct zone* z = &G_alloc.zones[order];
  struct free_item* item = (struct free_item*)ptr;
  item->next = z->free_list;
  z->free_list = item;
  z->free_size += BLOCK_SIZE << order;
}

void split_zone(void* ptr, int order) {
  int next_order = order + 1;
  char* half = (char*)ptr + (BLOCK_SIZE << order);
  free_zone(half, next_order);
}

void* alloc(int size) {
  int order = ceil(log2(size));
  return alloc_zone(order);
}

void free(void* ptr, int size) {
  int order = ceil(log2(size));
  free_zone(ptr, order);
}
核心记忆分配器

核心记忆分配器是一种内存分配器,它是操作系统内核中的一个模块,用于将物理内存分配给进程。当进程需要分配内存时,它调用核心记忆分配器来获取虚拟地址空间中的一部分。

在 Linux 操作系统中,内存分配通常是通过 sbrk 系统调用进行的。 sbrk 系统调用增加了进程的数据段结束位置,从而增加了可用的堆空间。另一方面,当进程释放堆空间时, sbrk 将数据段结束位置向下移动,减少可用的堆空间。

以下是一个简单的使用 sbrk 系统调用的代码片段:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
  void* ptr = sbrk(0); // 获取当前数据段的结束位置
  int size = 1024;
  sbrk(size); // 增加数据段的大小
  void* ptr2 = sbrk(0); // 获取当前数据段的结束位置
  printf("ptr: %p, ptr2: %p, diff: %d\n", ptr, ptr2, (int)ptr2 - (int)ptr);
  sbrk(-size); // 减少数据段的大小
  return 0;
}
总结

本文介绍了二次幂的自由列表分配器和核心记忆分配器。二次幂的自由列表分配器是一种实现动态内存分配的算法,它根据用户需求的大小分配最接近的空闲内存块。核心记忆分配器是操作系统内核中的模块,用于分配物理内存给进程。这两种内存分配器都是程序员的利器,可以帮助开发高效和可靠的应用程序。