Linux的ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM及分配页释放页函数的简单介绍
- 简单介绍一下页:
- Linux 区:
- 分配页系统调用
- 释放页系统调用
简单介绍一下页:
内核把物理页作为内存管理的基本单位。 尽管处理器的最小可寻址单位通常为字(甚至字节),但是,内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址的硬件)通常以页为单位进行处理。正因为如此,MMU以页(page)大小为单位来管理系统中的页表(这也是页表名的来由)。从虚拟内存的角度来看,页就是最小单位。
内核用struct page结构表示系统中的每个物理页,该结构位于<linux/mm.h>中。
让我们看一下其中比较重要的域。
flag域 :用来存放页的状态。这些状态包括页是不是脏的,是不是被锁定在内存中等等。flag的每一位单独表示一种状态,所以它至少可以同时表示出32种不同的状态。这些标志定义在<linux/page-flags.h>中。
_count域:存放页的引用计数——也就是这一页被引用了多少次。当计数值变为0时,就说明当前内核并没有引用这一页,于是,在新的分配中就可以使用它。
virtual域:是页的虚拟地址。通常情况下,它就是页在虚拟内存中的地址。有些内存(即所谓的高端内存)并不永久地映射到内核地址空间上。在这种情况下,这个域的值为NULL,需要的时候,必须动态地映射这些页。
Linux 区:
由于硬件的限制,内核并不能对所有的页一视同仁。有些页位于内存中特定的物理地址上,所以不能将其用于一些特定的任务。由于存在这种限制,所以内核把页划分为不同的区(zone)。内核使用区对具有相似特性的页进行分组。Linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:
- 一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问)。
- 一些体系结构其内存的物理寻址范围比虚拟寻址范围大得多。这样,就有一些内存不能永久地映射到内核空间上。
因为这些原因:所以Linux使用了三种区:(注意,区的划分没有任何物理意义﹔这只不过是内核为了管理页而采取的一种逻辑上的分组。)
ZONE_DMA——这个区包含的页能用来执行DMA操作。ZONE_NORMAL–—这个区包含的都是能正常映射的页。
ZONE_HIGHMEM一一这个区包含“高端内存”,其中的页并能不永久地映射到内核地址空间。
区 | 描述 | 物理内存 |
---|---|---|
ZONE_DMA | DMA使用的页 | 小于16MB |
ZONE_NORMAL | 正常可寻址的页 | 16~896MB |
ZONE_HIGHMEM | 动态映射的页 | 大于 896MB |
Linux把系统的页划分为区,形成不同的内存池,这样就可以根据用途进行分配了。例如,ZONE_DMA内存池让内核有能力为DMA分配所需的内存。如果需要这样的内存,那么,内核就可以从ZONE_DMA中按照请求的数目取出页。
尽管某些分配可能需要从特定的区中获取页,但是,某种用途的内存不一定要从对应的区获取。
尽管用于DMA的内存必须从ZONE_DMA中进行分配,但是一般用途的内存却既能从ZONE_DMA分配,也能从ZONE_NORMAL分配。当然,内核更希望般用途的内存从常规区分配,这样能节省ZONE_DMA中的页,保证满足DMA的使用需求。但是,如果可供分配的资源不够用了(如果内存已经变得很少了),那么,内核就会去占用其他可用区的内存。
每个区都用struct zone表示,定义在<linux/mmzone.h>中。
分配页系统调用
struct page * alloc_pages(unsigned int gfp_mask,unsigned int order)
该函数分配2^order(即1<<order)个连续的物理页,并返回一个指针,该指针指向第一个页的page结构体;如果出错,就返回NULL。
unsigned long__get_free_pages(unsigned int gfp_mask,unsigned int order)
这个函数与alloc_pages()作用相同,不过它返回的是所请求的第一个页的逻辑地址。因为页是连续的,因此其他页也会紧随其后。
unsigned longget_zeroed_page(unsigned int gfp_mask)
这个函数与_get_free_page()工作方式相同,只不过把分配好的页都填充成了0。
释放页系统调用
void _free_pages(struct page *page, unsigned int order)
void free_pages (unsigned long addr,unsigned int order)
void free page ( unsigned long addr)
使用时一定要注意,如果传递了错误的struct page或地址,用了错误的order值,这些都可能导致系统崩溃。因为这些函数都是运行在内核空间的系统调用。