snifer

【原创】基于嵌入式系统的内存区域Zone

0
阅读(1706)
Linux通过伙伴算法管理和分配页,但由于硬件的原因,内存中的不同区域会有不同的特性。主要有以下两个问题:
1)一些硬件只能用某些特定的内存地址来执行DMA;
2)一些体系结构中有一些内存不能永久映射到内核空间上。
}因此某些内存必须从特定区域上分配,不能由单一的伙伴系统管理。为了区分这些内存区域,Linux使用了3个Zone, 每个Zone由一个自己的伙伴系统来管理:
lZONE_DMA 包含可以用来执行DMA操作的内存
lZONE_NORMAL 包含可以正常映射到虚拟地址的内存区域
lZONE_HIGHMEM 包含不能永久映射到内核地址空间的内存区域

这些区域的分划和具体的体系结构有关,例如某些体系结构中ZONE_NORMAL覆盖了所有的内存区域,而另外两个区均为空。而在x86上,ZONE_DMA为0~16MB的内存范围,ZONE_HIGHMEM包含了所有高于896MB的物理内存,ZONE_NORMAL覆盖其余部分。这个规定可在<linux/mmzone.h>中找到。


系统中对于物理页有大量的需求,当程序映像加载到内存中的时候,操作系统需要分配页。当程序结束执行并卸载时需要释放这些页。另外为了存放核心相关的数据结构比如页表自身,也需要物理页。这种用于分配和回收页的机制和数据结构对于维护虚拟内存子系统的效率也许是最重要的。
系统中的所有的物理页都使用page数据结构来描述。每一个物理页对应一个page变量。一个zone的所有page变量集合形成数组,由zone的zone_mem_map成员指针指向数组的起始地址,页数组的初始化在系统启动时完成。
页分配器的算法是伙伴统之上的。伙伴系统将内存区域组织为以页为单位的块,n称为该块的“级别”,具有相同级别的块用链表接在一起。每次分配必须指定一个级别,并以该级别块的大小为单位。
在分配时,依次从级别n到最大级别开始搜索,直到找到一个非空的块为止,如果这个非空块级别不是n, 则将其拆成两份,一份放到其对应的级别的空闲块中,而另一份如果还不是级别n就继续拆分。直到最后返回那个级别为n的块。

在回收时,首先计算出被回收的块的伙伴,然后查看这个伙伴是否在级别为n的空闲链中。如果找到了,则这个块将与这个伙伴合并(伙伴从空闲链删除,并修整“当前块”的位置即可),然后n := n+1,继续这个合并过程。当伙伴不在改空闲链中时,合并过程结束。free_area所管理的内存如下图所示。


上面的算法可以非常有效地分配和回收内存,但同时也限制了分配的灵活性(页面数必须是2的指数). 在zone中与这个算法相关的field主要是:
struct free_area    free_area[MAX_ORDER];
这里就是各个级别的空闲链的入口。free_area结构是一个非常简单的结构,其中包括一个链表项和该级别的链表元素数目:
struct free_area {
    struct list_head    free_list;
    unsigned long       nr_free;
};