__get_vm_area函数功能描述:__get_vm_area( )函数查找一块从start开始到end结束的线性地址,并从该地址块中创建size字节大小的内核虚拟区间。
__get_vm_area文件包含
#include <linux/vmalloc.h>
__get_vm_area函数定义
在内核源码中的位置:linux-3.19.3/mm/vmalloc.c
函数定义格式:
struct vm_struct *__get_vm_area(unsigned long size, unsigned long f lags, unsigned long start, unsigned long end)
__get_vm_area输入参数说明
size
:是指要创建的内存虚拟区间的大小。flags
:是指非连续地址空间的映射方式。start
和end
分别指要查找线性地址块的开始结束位置。
其中f lags可取值定义见文件linux-3.19.3/include/linux/vmalloc.h,如下:
#define VM_IOREMAP 0x00000001 //通过函数ioremap( )分配的页
#define VM_ALLOC 0x00000002 //通过vmalloc( )分配的页
#define VM_MAP 0x00000004 //通过vmap( )映射的已经分配的页
#define VM_USERMAP 0x00000008 //适用于remap_vmalloc_range( )分配的页
#define VM_VPAGES 0x00000010 //将被分配的缓冲区的页
#define VM_UNINITIALIZED 0x00000020 //vm_struct结构体变量没有被完全初始化
其中,vm_struct结构体是内核虚拟区间描述符,它在内核文件linux-3.19.3/include/linux/vmalloc.h中定义,这里对该结构体的部分字段进行说明:
struct vm_struct {
struct vm_struct *next; //下一个vm用来形成链表
void *addr; //虚拟地址
unsigned long size; //vm的大小
unsigned long flags; //vm的标志
struct page **pages; //vm所映射的page
unsigned int nr_pages; //page个数
phys_addr_t phys_addr; //对应的起始物理地址
const void *caller;
};
__get_vm_area返回参数说明
__get_vm_area( )函数返回一个指针,该指针指向所分配的内核虚拟区间的描述符,如果创建虚拟区间不成功,则返回NULL。
__get_vm_area实例解析
编写测试文件:__get_vm_area.c
头文件及全局变量声明如下:
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
static int __init __get_vm_area_init(void);
static void __exit __get_vm_area_exit(void);
struct vm_struct *vm = NULL;
模块初始化函数:
int __init __get_vm_area_init(void)
{
vm = __get_vm_area(8192*4, VM_ALLOC, 0xC0000100, 0xdd000000);
if( ! vm )
{
return -ENOMEM;
}
else
{
printk("vm->size :%ld\n", vm->size); //输出内核线性区间大小
printk("vm->addr :0x%lx\n", (unsigned long)vm->addr); //输出起始地址
}
return 0;
}
模块退出函数:
void __exit __get_vm_area_exit(void)
{
if(vm)
{
free_vm_area(vm);
printk("free_vm_area ok! \n");
}
printk("exit ! \n");
}
模块初始化及退出函数调用:
module_init(__get_vm_area_init);
module_exit(__get_vm_area_exit);
实例运行结果及分析:
首先编译模块,执行命令insmod __get_vm_area.ko插入模块,然后执行命令dmesg -c,会出现如图所示的结果。
执行命令rmmod __get_vm_area.ko卸载模块,执行命令dmesg -c,会出现如图所示的结果。
结果分析:
由传入__get_vm_area( )函数的实参可知,该实例创建一个8192*4
大小的内核虚拟区间,该地址区间是按照VM_ALLOC映射方式在0xc0000100
和0xdd000000
之间的线性地址块中分配的。输出值36864是8192*4
加上4096后得到的,在调用__get_vm_area( )分配内核虚拟地址时,内核都会在原本大小的基础上再加一个保护页给返回值vm_struct,所以会得到vm->size = 8192*4+ PAGE_SIZE(4096)
。vm->addr
即0xc000a100
分配所得的区间第一块内存块的线性地址。最后在模块退出时调用free_vm_area( )函数用来释放由__get_vm_area( )函数创建的内核虚拟区间,参考该函数的分析。