内核把物理页作为内存管理的基本单位。
32位系统:4K / 页
64位系统:8K / 页
由于硬件限制,内核不能对所有页一视同仁。linux必须处理如下硬件存在的缺陷而引起的内存寻址问题:
linux主要使用了四种区:
ZONE_DMA —— 这个区包含的页只能用来执行DMA操作
ZONE_DMA32 —— 和ZONE_DMA类似,但只能被32位设备访问
ZONE_NORMAL —— 这个区包含的页都是能正常映射的页
ZONE_HIGHEM —— 这个区包含“高端内存”,不能永久映射到内核地址空间
区 | 描述 | 物理内存 |
ZONE_DMA | DMA使用的页 | < 16M |
ZONE_NORMAL | 正常寻址的页 | 16M ~ 896M |
ZONE_HIGHEM | 动态映射的页 | > 896M |
函数 | 描述 |
struct page * allock_page(gfp_mask) | 只分配一页,返回指向页结构的指针 |
struct page * allock_page(gfp_mask, order) | 分配2的order次方个页,返回指向第一个页结构的指针 |
unsigned long __get_free_page(gfp_mask) | 只分配一页,返回指向企逻辑地址的指针 |
unsigned long __get_free_pages(gfp_mask, order) | 分配2的order次方个页,返回指向第一页逻辑地址的指针 |
unsigned long get_zoned_page(gfp_mask) | 只分配一页,让其内容填充0,返回指向企逻辑地址的指针 |
函数原型 | 描述 |
void __free_pages(struct page * page, unsigned int order) | 释放page页地址开始的2的order次方个页 |
void free_pages(unsigned long addr, unsigned int order) | 释放addr地址开始的2的order次方个页 |
void free_page(unsigned long addr) | 释放addr地址开始的一个页 |
struct page * allock_pages(gfp_t gfp_mask, unsigned int order)
该函数分配2的order次方个连续的物理页,并返回指向第一个页的page结构体指针;如果出错返回NULL。
下面函数把给定的页转换成它的逻辑地址:
void * page_address(struct page * page)
e.g.获得8个页,然后释放:
unsigned long page;
page = __get_free_pages(GFP_KERNEL, 3); //申请8个页
if (!page)
{
return -ENOMEM; //没有足够内存
}
free_pages(page, 3); //释放
GFP_KERNEL是gfp_mask标志的一种,后边有说明
函数原型 | 描述 |
void * kmalloc(size_t, gfp_t flags) | 按照flags标志分配至少size字节大小的内存,且物理连续,返回指向内存块的指针 |
void * vmalloc(unsigned long size) | 按照flags标志分配至少size字节大小的内存,虚拟地址连续,物理地址不一定连续,返回指向内存块的指针 |
void kfree(const void *ptr) | 释放由kmalloc申请的内存空间 |
void vfree(const void *addr) | 释放由vmalloc申请的内存空间 |
kmalloc 和 vmalloc的区别在于:
kmalloc()分配的是物理地址连续的内存(逻辑地址自然也是连续的),该函数是否允许休眠通过其参数flags来决定。
vmalloc()函数只确保页在虚拟地址空间内是连续的,它通过分配非连续的物理内存块,再“修正”页表,把内存映射到逻辑地址空间的连续区域。
因此,该函数可能睡眠,不能在中断上下文进行调用,也不能从其他不允许组赛的情况下调用。
出于性能考虑,内核代码多用kmalloc()来获得内存。
不管是在低级页分配函数中,还是在kmalloc()中,都用到了分配器标志。这些标志可分为三类:行为修饰符、区段修饰符及类型。
行为修饰符表示你内核该如何分配所需内存,如:中断处理程序需要内核分配内存过程中不能睡眠。
标志 | 描述 |
__GFP_WAIT | 分配器可以睡眠 |
__GFP_IO | 分配器可以启动磁盘IO |
__GFP_FS | 分配器可以启动文件系统IO |
__GFP_HIGH | 分配器可以访问紧急事件缓冲池 |
区修饰符表示从哪个区中进行分配,ZONE_DMA、ZONE_NORMAL还是ZONE_HIGHEM。
标志 | 描述 |
__GFP_DMA32 | 从ZONE_DMA分配 |
__GFP_DMA32 | 只在ZONE_DMA32分配 |
__GFP_HIGHMEM | 从ZONE_HIGHMEM或ZONE_NORMAL分配 |
类型标志是行为修饰符和区修饰符的组合,如:GFP_KERNEL = (__GFP_WAIT | __GFP_IO | __GFP_FS)
标志 | 描述 |
GFP_ATOMIC | 这个标志用在中断处理程序、下半部、持有自旋锁以及其他不能睡眠的地方 |
GFP_KERNEL | 这是一种常规分配方式,可能会阻塞。这个标志在睡眠安全时用在进程上下文代码中, 为了获得调用者所需的内存,内核会尽力而为。这个标志应当是首选标志 |
请参考:
http://blog.sina.com.cn/s/blog_65373f1401017o4v.html