关于linux内存管理的概念请参考笔者前面的博文:
linux内存管理(一)基础篇
linux内存管理(二)伙伴算法
linux内存管理(三)slab分配器
下面的驱动程序中的函数的区别,以及对应的函数介绍请参考第一个链接(linux内存管理基础篇)
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/semaphore.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/mm.h>
MODULE_LICENSE("Dual BSD/GPL");
#define DEBUG_SWITCH 1
#if DEBUG_SWITCH
#define P_DEBUG(fmt, args...) printk("<1>" "<kernel>[%s]"fmt,__FUNCTION__, ##args)
#else
#define P_DEBUG(fmt, args...) printk("<7>" "<kernel>[%s]"fmt,__FUNCTION__, ##args)
#endif
#define DEV_SIZE 20//方便测试写进程阻塞
#define WQ_MAJOR 230
struct page *p;
char *s, *kp;
static int __init wq_init(void)
{
unsigned long virt, phys;
kp = (char *)kmalloc(10, GFP_KERNEL);//分配10大小,实际上会分配16字节大小的内存,满足2^n
if(NULL == kp)
{
P_DEBUG("kmalloc error\n");
return -ENOMEM;
}
memcpy(kp, "hello world", 16);//将数据拷贝到分配的内存,这里故意大于10,为了测试
printk("kello kernel, %s\n", kp);
#define SWITCH 0
#if SWITCH
p = alloc_pages(GFP_KERNEL, 1);//2^1 = 2,分配两个页面大小,返回的是物理地址,不能直接用p访问
if(NULL == p)
{
P_DEBUG("alloc_pages error\n");
return -ENOMEM;
}
s = page_address(p);//将其转换为对应的虚拟地址,这样可以访问
#else
s = (char *)__get_free_pages(GFP_KERNEL, 1);//分配2^1个页面,返回逻辑地址
if(NULL == s)
{
P_DEBUG("__get_free_pages error\n");
return -ENOMEM;
}
#endif
phys = __pa((unsigned long)s);//通过虚拟地址获得对应的物理地址
virt = (unsigned long)__va(phys);//通过物理地址获得对应的虚拟地址
printk("virtual address: %p\n", s);//打印分配后的虚拟地址
printk("phys address: %p\n", (void *)phys);//打印转换后的物理地址
printk("virt address: %p\n", (void *)virt);//打印再转换后的虚拟地址
memcpy(s, "hello world", 16);//拷贝数据
printk("hello kernel, %s\n", s);
return 0;
}
static void __exit wq_exit(void)
{
vfree(kp);//释放
#if SWITCH
__free_pages(p, 1);//释放函数必须与分配函数一一对应
#else
free_pages((unsigned long)s, 1);
#endif
printk("good bye kernel\n");
}
module_init(wq_init);
module_exit(wq_exit);
测试:直接加载模块,然后dmesg就可以看到结果。