alloc_pages函数功能描述:alloc_pages( )函数以gfp_mask分配方式分配2的order次方(1<<order)个连续的物理页。
alloc_pages文件包含
#include<linux/gfp.h>
alloc_pages函数定义
在内核源码中的位置:linux-3.19.3/include/linux/gfp.h
函数定义格式:
static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order)
alloc_pages输入参数说明
gfp_mask:是分配标志,内核分配内存有多种方式,该参数告诉内核如何分配以及在哪分配所需的内存,内存分配最终总是调用__get_free_pages( )来实现,这也是GFP_前缀的由来。其中分配标志(gfp_mask)的取值定义见文件linux-3.19.3/include/linux/gfp.h可以取以下各值:
- GFP_KERNEL:该分配方式最常用,是内核内存的正常分配,它可能睡眠。
- GFP_ATOMIC:该分配方式常用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。
- GFP_USER:用来为用户空间分配内存页,可能睡眠。
- GFP_HIGHUSER:类似GFP_USER,如果有高端内存,就从高端内存分配页。
- GFP_NOIO、GFP_NOFS:功能类似于GFP_KERNEL,但是为内核分配内存的工作增加了限制。具有GFP_NOFS的分配不允许执行任何文件系统调用,而GFP_NOIO禁止任何I/O初始化。它们主要用在文件系统和虚拟内存代码,那里允许分配休眠,但不应发生递归的文件系统调用。
有的标志用双下划线作为前缀,它们可与上面标志“或”起来使用,以控制分配方式:
- __GFP_DMA:要求分配可用于DMA的内存。
- __GFP_HIGHMEM:分配的内存可以位于高端内存。
- __GFP_NOWARN:当一个分配无法满足,阻止内核发出警告(使用printk )。
- __GFP_HIGH:高优先级请求,允许为紧急状况消耗被内核保留的最后一些内存页。__GFP_REPEAT、__GFP_NOFAIL、__GFP_NORETRY:告诉分配器当满足一个分配有困难时,如何动作。__GFP_REPEAT表示努力再尝试一次,仍然可能失败;__GFP_NOFAIL告诉分配器尽最大努力来满足要求,始终不返回失败,不推荐使用;__GFP_NORETRY告知分配器如果无法满足请求,立即返回。
order:指要释放的物理页数,其取值为2的order次方个。
alloc_pages返回参数说明
alloc_pages( )函数返回page结构体指针,指向所分配的物理页中的第一个页,如果分配不成功,则返回NULL。
内核用struct page结构表示系统中的每个页框,结构体page在内核文件linux-3.19.3/include/linux/mm_types.h中定义。
alloc_pages实例解析
编写测试文件:alloc_page.c
头文件及全局变量声明如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/mm_types.h>
#include <linux/mm.h>
MODULE_LICENSE("GPL");
static int __init alloc_pages_init(void);
static void __exit alloc_pages_exit(void);
struct page * pages = NULL;
模块初始化函数:
int __init alloc_pages_init(void)
{
pages = alloc_pages(GFP_KERNEL,3); //分配8个物理页
if(! pages)
{
return -ENOMEM;
}
else
{
printk("alloc_pages Successfully! \n");
printk("page_address(pages) = 0x%lx\n", (unsigned long)page_address(pages));
}
return 0;
}
模块退出函数:
void __exit alloc_pages_exit(void)
{
if(pages)
{
__free_pages(pages,3); //释放所分配的8个页
printk("__free_pages ok! \n");
}
printk("exit\n");
}
模块初始化及退出函数调用:
module_init(alloc_pages_init);
module_exit(alloc_pages_exit);
实例运行结果及分析:
首先编译模块,执行命令insmod alloc_pages.ko插入模块,然后执行命令dmesg -c,会出现如图所示的结果。
执行命令rmmod alloc_pages.ko卸载模块,再执行命令dmesg -c,会出现如图所示的结果。
结果分析:
测试程序中调用了内核函数alloc_pages( )分配2的3次方即8个连续的物理页面,返回指针赋值给pages,指向所分配的物理页中第一个页的结构体。之后通过输出pages相关的一些信息来获取页的分配情况。
输出pages的虚拟地址为0xffff88013e228000。在模块退出时调用__free_pages( )函数释放以pages指针所指向的页为首的2的3次方个连续的物理页面,参考本教程关于该函数的分析。