首页 百科知识 内存分配与释放算法

内存分配与释放算法

时间:2022-10-09 百科知识 版权反馈
【摘要】:加在页面块前部的信息头称为页描述符,定义在/mm/kmalloc.c中:Linux设置了sizes[]数组,对页面块进行描述。priority取值GPF_DMA,表示申请的内存用于DMA传送。当内存不够时,priority取值GFP_KERNEL表示当前申请进程暂时被挂起而等待换页;取值GPF_ATOMIC时,表示该函数不允许推迟,而立即返回0值。由sizes[]管理的内存中有1页块、2页块或4页块被释放时,它们不立即交还free_area[]管理,先交给kmalloc_cache[]管理。在申请和释放较小且连续的内存空间时,使用kmalloc()和kfree()在物理内存中进行分配。

4.5.2 内存分配与释放算法

内存分配与释放算法kmalloc()和kfree()分配和释放内存是以块(block)为单位进行的。

(1)内存分配与释放的数据结构

可以分配的空闲块的大小记录在blocksize表中,它是一个静态数组,定义在/mm/kmalloc.c中:

#if PAGE_SIZE == 4096

static const unsigned int blocksize[] = {

    32,64,128,252,508,1020,2040,

    4096-16,8192-16,16384-16,

    32768-16,65536-16,131072-16,

    0

};

在使用kmalloc()分配空闲块时仍以Buddy算法为基础,即以free_area[]管理的空闲页面块作为分配对象。重新制定了分配的单位,blocksize[]数组中的块长度。它可以分配比1个页面更小的内存空间。blocksize[]中的前7个是在1个空闲页面内进行分配,其后的6种分别对应free_area[]的1至32空闲页面块,当申请分配的空间小于或等于1个页面时,从free_area[]管理的1页面块中查找空闲页面进行分配。若申请的空间大于1个页面时,按照blocksize[]后六个块单位进行申请,从free_area[]中与该块长度对应的空闲页块组中查找空闲页面块。

对kmalloc()分配的内存页面块中加上一个信息头,它处于该页面块的前部。页面块中信息头后的空间是可以分配的内存空间。加在页面块前部的信息头称为页描述符,定义在/mm/kmalloc.c中:

struct page_descriptor {

  struct page_descriptor *next;  /* 指向下一个页面块的指针 */

  struct block_header *firstfree; /* 本页中空闲块链表的头 */

  int order;            /* 本页中块长度的级别 */

  int nfree;            /* 本页中空闲的数目 */

};

具有相同块单位和使用特性的页面块组成若干个链表。

Linux设置了sizes[]数组,对页面块进行描述。数组元素是size_descriptor结构体,定义在/mm/kmalloc.c中:

struct size_descriptor {

  struct page_descriptor *firstfree; /* 一般页块链表的头指针 */

  struct page_descriptor *dmafree; /* DMA页块链表的头指针 */

  int nblocks;      /* 页块中划分的块数目 */

  int nmallocs;      /* 链表中各页块中已分配的块总数 */

  int nfrees;       /* 链表中各页块中尚空闲的块总数 */

  int nbytesmalloced;  /* 链表中各页块中已分配的字节总数 */

  int npages;      /* 链表中页块数目 */

  unsigned long gfporder; /* 页块的页面数目 */

};

static struct size_descriptor sizes[] ={

  {NULL, NULL, 127, 0, 0, 0, 0, 0},

  {NULL, NULL, 63, 0, 0, 0, 0, 0},

  {NULL, NULL, 31, 0, 0, 0, 0, 0},

  {NULL, NULL, 16, 0, 0, 0, 0, 0},

  {NULL, NULL, 8, 0, 0, 0, 0, 0},

  {NULL, NULL, 4, 0, 0, 0, 0, 0},

  {NULL, NULL, 2, 0, 0, 0, 0, 0},

  {NULL, NULL, 1, 0, 0, 0, 0, 0},

  {NULL, NULL, 1, 0, 0, 0, 0, 1},

  {NULL, NULL, 1, 0, 0, 0, 0, 2},

  {NULL, NULL, 1, 0, 0, 0, 0, 3},

  {NULL, NULL, 1, 0, 0, 0, 0, 4},

  {NULL, NULL, 1, 0, 0, 0, 0, 5},

  {NULL, NULL, 0, 0, 0, 0, 0, 0}

};

blocksize[]与sizes[]元素数目相同,它们一一对应。由kmalloc()分配的每种块长度的页面块链接成两个链表,一个是DMA可以访问的页面块链表,dmafree指向这个链表。一个是一般的链表,firstfree指向这个链表。成员项gfporder是0~5,它作为2的幂数表示所含的页面数。

由sizes[]管理的各个页面块中每个块(空闲块和占用块)的头部还有一个对该块进行描述的块头block_header:

struct block_header {

 unsigned long bh_flags; /* 块的分配标志 */

 union {

  unsigned long ubh_length;   /* 块长度 */

  struct block_header *fbh_next; /*指向下一空闲块的指针 */

 } vp;

};

bh_flages是块的标志,有三种:

MF_FREE指明该块是空闲块;

MF_USED表示该块已占用;

MF_DMA 表示该块是DMA可访问。

ubh_length和fbh_next是联合体成员项,当块占用时使用ubh_length表示该块的长度;

当块空闲时使用fbh_next链接下一个空闲块。

在一个页块中的空闲块组成一个链表,表头由页块的page_descriptor中firstfree指出。

(2)内存分配函数kmalloc()和释放函数kfree()

void *kmalloc(size_t size, int priority)

参数size是申请分配内存的大小,priority是申请优先级。

priority常用的值为GFP_KERNEL、GFP_ATOMIC和GPF_DMA。

priority取值GPF_DMA,表示申请的内存用于DMA传送。当内存不够时,priority取值GFP_KERNEL表示当前申请进程暂时被挂起而等待换页;取值GPF_ATOMIC时,表示该函数不允许推迟,而立即返回0值。

kfree()用于释放由kmalloc()分配的内存空间:

void kfree(void *__ptr)

ptr是kmalloc()分配的内存空间的首地址

img22

图4-8 sizes[]、kmalloc_cache[]和free_area[]的关系

当kmalloc管理的一个页面块中的占用块全部被释放后,它就成为一个空闲页面块。系统把这个空闲页面块从sizes[]管理的相应链表中删除,把它交给free_area[]数组按照buddy算法管理。kmalloc()和kfree()还共同维护一个kmalloc缓冲区,由kmalloc_cache的数组进行管理,定义如下:

#define MAX_CACHE_ORDER 3

struct page_descriptor * kmalloc_cache[MAX_CACHE_ORDER];

kmalloc_cache[]有3个元素,分别指向一个空闲的1、2、4页面块。由sizes[]管理的内存中有1页块、2页块或4页块被释放时,它们不立即交还free_area[]管理,先交给kmalloc_cache[]管理。sizes[]中有新的1页块、2页块或4页块被释放时,把kmalloc_cache[]当前指向的空闲页块交给free_area[]管理,然后指向新释放的空闲页块。三者的关系见图4-8。

(3)虚拟内存的申请和释放

在申请和释放较小且连续的内存空间时,使用kmalloc()和kfree()在物理内存中进行分配。申请较大的内存空间时,使用vmalloc()。由vmalloc()申请的内存空间在虚拟内存中是连续的,它们映射到在物理内存时,可以使用不连续的物理页面,而且仅把当前访问的部分放在物理页面中。由vmalloc()分配的虚存空间称为虚拟内存块(虚存块)。由vmalloc()分配的虚存块用一个链表来管理,系统定义的指针变量vmlist指向链表的表头,在mm/vmalloc.c中定义如下:

static struct vm_struct * vmlist = NULL;

结构vm_struct描述由vmalloc()分配虚存块:

struct vm_struct {

  unsigned long flags;  /* 虚存块的标志 */

  void * addr;      /* 虚存块起始地址 */

  unsigned long size;   /*虚存块大小 */

  struct vm_struct * next; /* 指向下一个虚存块的指针 */

};

vmalloc()和vfree()定义在mm/vmalloc.c中:

void * vmalloc(unsigned long size)

void vfree(void * addr)

可以看到vmalloc()参数size指出申请内存的大小。分配成功后,返回值为在虚存空间分配的虚存块首地址,失败返回值为0。vfree()用来释放由vmalloc()分配的虚存块,参数addr是要释放的虚存块首址。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈