Linux内核API __get_vm_area

__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:是指非连续地址空间的映射方式。
  • startend分别指要查找线性地址块的开始结束位置。

其中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,会出现如图所示的结果。

Linux内核API __get_vm_area

执行命令rmmod __get_vm_area.ko卸载模块,执行命令dmesg -c,会出现如图所示的结果。

Linux内核API __get_vm_area

结果分析:

由传入__get_vm_area( )函数的实参可知,该实例创建一个8192*4大小的内核虚拟区间,该地址区间是按照VM_ALLOC映射方式在0xc00001000xdd000000之间的线性地址块中分配的。输出值36864是8192*4加上4096后得到的,在调用__get_vm_area( )分配内核虚拟地址时,内核都会在原本大小的基础上再加一个保护页给返回值vm_struct,所以会得到vm->size = 8192*4+ PAGE_SIZE(4096)vm->addr0xc000a100分配所得的区间第一块内存块的线性地址。最后在模块退出时调用free_vm_area( )函数用来释放由__get_vm_area( )函数创建的内核虚拟区间,参考该函数的分析。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程