Linux内核模块vmalloc和kmalloc系统调用的代码实战

Linux内核模块vmalloc和kmalloc系统调用的代码实战

  • 一、前言
  • 二、编程接口
    • 2.1、内核提供的vmalloc函数接口
    • 2.2、内核提供的kmalloc函数接口
    • 2.3、数据结构
  • 三、vmalloc的使用示例
  • 四、kmalloc的使用示例

一、前言

当设备长时间运行后,内存碎片化,很难找到连续的物理页。在这种情况下,如果需要分配长度超过一页的内存块,可以使用不连续页分配器,分配虚拟地址连续但是物理地址不连续的内存块。在 32 位系统中不连分配器还有一个好处:优先从高端内存区域分配页,保留稀缺的低端内存区域。

二、编程接口

2.1、内核提供的vmalloc函数接口

  • vmalloc:分配不连续的物理地址空间,但虚拟内存地址是连续的。

  • vfree:配套,释放 vmalloc 分配的内存地址。

2.2、内核提供的kmalloc函数接口

  • kmalloc:分配物理连续的内存地址(则虚拟地址自然连续,基于 slab)。

  • kfree:配套,释放 kmalloc 分配的内存地址。

2.3、数据结构

struct vm_struct {
	struct vm_struct	*next;
	void			*addr;
	unsigned long		size;
	unsigned long		flags;
	struct page		**pages;
	unsigned int		nr_pages;
	phys_addr_t		phys_addr;
	const void		*caller;
};

struct vmap_area {
	unsigned long va_start;
	unsigned long va_end;

	struct rb_node rb_node;         /* address sorted rbtree */
	struct list_head list;          /* address sorted list */

	/*
	 * The following three variables can be packed, because
	 * a vmap_area object is always one of the three states:
	 *    1) in "free" tree (root is vmap_area_root)
	 *    2) in "busy" tree (root is free_vmap_area_root)
	 *    3) in purge list  (head is vmap_purge_list)
	 */
	union {
		unsigned long subtree_max_size; /* in "free" tree */
		struct vm_struct *vm;           /* in "busy" tree */
		struct llist_node purge_list;   /* in purge list */
	};
};
  • 每个虚拟内存区域对应一个 vmap_area 实例;
  • 每个 vmap_area 实例关联一个 vm_struct 实例;

三、vmalloc的使用示例

分配不连续的物理地址空间,但虚拟内存地址是连续的。

  1. 定义初始化模块和退出模块函数。
  2. 定义一个全局变量。
  3. 在初始化模块函数调用vmalloc函数,申请内存。
  4. 退出模块调用vfree释放内存。
  5. 模块初始化操作和退出函数调用module_init()和module_exit()。
  6. 其他的声明信息,比如许可协议、作者、模块功能描述等等。

vmtest.c

/* 头文件和全局变量地声明*/
#include 
#include 
#include 

static int __init vmalloc_InitFunc(void);
static void __exit vmalloc_ExitFunc(void);

#define MEMORY_SIZE 4096
char * pmymemory;

// 模块初始化函数
int __init vmalloc_InitFunc(void)
{
    pmymemory = (char *)vmalloc(MEMORY_SIZE);
    if(pmymemory == NULL )
        printk("执行:vmalloc(...)函数分配内存失败! \n");
    else
        printk("执行:vmalloc(...)函数成功,地址 = 0x%lx\n", (unsigned long)pmymemory);
    return 0;
}

// 模块退出函数
void __exit vmalloc_ExitFunc(void)
{
    if(NULL != pmymemory)
    {
        vfree(pmymemory);
        printk("调用:vfree(...)释放内存成功!\n");
    }

    printk("正常:内核模块退出成功!\n");
}

/* 模块初始化操作和退出函数调用 */
module_init(vmalloc_InitFunc);
module_exit(vmalloc_ExitFunc);

MODULE_LICENSE("GPL"); /* 描述模块代码接受的软件许可协议 */
MODULE_AUTHOR("Lion Long"); /* 描述模块的作者信息:包括作者姓名及邮箱等等 */
MODULE_DESCRIPTION(" kernel module : vmalloc/vfree"); /* 简要描述此模块用途及功能介绍*/

Makefile

obj-m:=vmtest.o	

CURRENT_PAHT:=$(shell pwd) 
LINUX_KERNEL:=$(shell uname -r)   

LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
all:

	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PAHT) modules

clean:

	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PAHT) cleals

(1)make。

$ make
make -C /usr/src/linux-headers-4.15.0-142-generic    M=/home/fly/workspace/vmalloctest  modules
make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
  CC [M]  /home/fly/workspace/vmalloctest/vmtest.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/fly/workspace/vmalloctest/vmtest.mod.o
  LD [M]  /home/fly/workspace/vmalloctest/vmtest.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'

$ ls
Makefile  modules.order  Module.symvers  vmtest.c  vmtest.ko  vmtest.mod.c  vmtest.mod.o  vmtest.o

(2)插入模块。

# insmod vmtest.ko 
# dmesg -c
[159699.561428] 执行:vmalloc(...)函数成功,地址 = 0xffffa6aec0668000

四、kmalloc的使用示例

分配物理连续的内存地址(则虚拟地址自然连续,基于 slab)。

  1. 定义初始化模块和退出模块函数。
  2. 定义一个全局变量。
  3. 在初始化模块函数调用kmalloc函数,申请内存。
  4. 退出模块调用kfree释放内存。
  5. 模块初始化操作和退出函数调用module_init()和module_exit()。
  6. 其他的声明信息,比如许可协议、作者、模块功能描述等等。

kmtest.c

/* 头文件和全局变量地声明*/
#include 
#include 
#include 
#include 

static int __init kmalloc_InitFunc(void);
static void __exit kmalloc_ExitFunc(void);

#define MEMORY_SIZE 4096
char * pmymemory;

// 模块初始化函数
int __init kmalloc_InitFunc(void)
{
    pmymemory = (char *)kmalloc(MEMORY_SIZE,GFP_KERNEL);
    if(pmymemory == NULL )
        printk("执行:kmalloc(...)函数分配内存失败! \n");
    else
    {
        // /*输出分配的内存空间的起始地址*/
        printk("执行:kmalloc(...)函数成功,地址 = 0x%lx\n", (unsigned long)pmymemory);
    }
        
    return 0;
}

// 模块退出函数
void __exit kmalloc_ExitFunc(void)
{
    if(NULL != pmymemory)
    {
        kfree(pmymemory);
        printk("调用:kfree(...)释放内存成功!\n");
    }

    printk("正常:内核模块退出成功!\n");
}

/* 模块初始化操作和退出函数调用 */
module_init(kmalloc_InitFunc);
module_exit(kmalloc_ExitFunc);

MODULE_LICENSE("GPL"); /* 描述模块代码接受的软件许可协议 */
MODULE_AUTHOR("Lingshengedu"); /* 描述模块的作者信息:包括作者姓名及邮箱等等 */
MODULE_DESCRIPTION(" kernel module : kmalloc/kfree"); /* 简要描述此模块用途及功能介绍*/

Makefile

obj-m:=kmkf.o	

CURRENT_PAHT:=$(shell pwd) 
LINUX_KERNEL:=$(shell uname -r)   

LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
all:

	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PAHT) modules

clean:

	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PAHT) cleals

(1)make。

# make
make -C /usr/src/linux-headers-4.15.0-142-generic    M=/home/fly/workspace/kmalloctest  modules
make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
  CC [M]  /home/fly/workspace/kmalloctest/kmtest.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/fly/workspace/kmalloctest/kmtest.mod.o
  LD [M]  /home/fly/workspace/kmalloctest/kmtest.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'

# ls
kmtest.c  kmtest.ko  kmtest.mod.c  kmtest.mod.o  kmtest.o  Makefile  modules.order  Module.symvers

(2)insmod。

# insmod kmtest.ko 
# dmesg -c
[160514.302923] 执行:kmalloc(...)函数成功,地址 = 0xffff8f54b28ac000

Linux内核模块vmalloc和kmalloc系统调用的代码实战_第1张图片

你可能感兴趣的:(Linux内核分析,linux,java,运维)