page_address宏功能描述:page_address( )宏的功能是获得物理页的逻辑地址。page_address( )的定义有多个版本,随内核编译时配置不同而不同。在笔者的机器上,page_address( )被定义成一个宏。
page_address文件包含
#include<linux/mm.h>
page_address宏定义
在内核源码中的位置:linux-3.19.3/include/linux/mm.h
宏定义格式:
#if ! defined(HASHED_VIRTUAL) && ! defined(WANT_PAGE_VIRTUAL)
#define page_address(page) lowmem_page_address(page)
#endif
page_address输入参数说明
参数page指向一个物理页框,它是该页的描述符指针。关于结构体page的定义参考alloc_pages( )函数的分析。
page_address返回值说明
返回值是一个逻辑地址,它也是所给页的虚拟地址。
page_address实例解析
编写测试文件:page_address.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 page_address_init(void);
static void __exit page_address_exit(void);
struct page * page = NULL;
模块初始化函数:
int __init page_address_init(void)
{
page = alloc_pages(GFP_KERNEL, 0); //分配一个物理页
if(! page)
{
printk("alloc_pages failed! \n");
return -ENOMEM;
}
else
{
printk("addr = 0x%lx\n", (unsigned long)page);
printk("virtual addr = 0x%lx\n", (unsigned long)page_address(page));
// 输出该page的逻辑地址
}
return 0;
}
模块退出函数:
void __exit page_address_exit(void)
{
if(page)
__free_pages(page,0); //释放分配的物理页
printk("exit! \n");
}
模块初始化及退出函数调用:
module_init(page_address_init);
module_exit(page_address_exit);
实例运行结果及分析:
首先编译模块,执行命令page_address.ko插入模块,然后执行命令dmesg -c,会出现如图所示的结果。
结果分析:
在测试程序中,首先调用alloc_pages( )分配一个物理页框,由输出结果可知,该物理页框描述符的起始地址为0xffffea00014e8300,然后调用page_address( )函数获得其逻辑地址为0xffff880053a0c000。最后在模块退出时,调用__free_pages( )函数释放分配的物理页。
关于函数alloc_pages( )和__free_pages( )的功能,参考极客笔记网这两个函数的分析。