转自:
    
     https://blog.csdn.net/u014792216/article/details/79020326
    
   
在内核申请一片物理内存,映射到用户空间使用的方法。环境:Linux ubuntu 4.10.0-42-generic。
    
     方法经博主测试,测试环境:ubuntu 16.04,内核版本:linux-4.15
    
   
    一、内核驱动模块
   
map.c:
#include <linux/init.h>  
#include <linux/kernel.h>  
#include <linux/module.h>  
#include <linux/fs.h>  
#include <linux/types.h>  
#include <linux/errno.h>  
#include <linux/fcntl.h>  
#include <linux/vmalloc.h>  
//#include <asm/uaccess.h>  
#include <asm/io.h>  
#include <asm/page.h>  
#include <linux/mm.h>  
#include <linux/platform_device.h>  
#include <linux/device.h>  
#include <linux/moduleparam.h>  
#include <linux/cdev.h>  
#include <linux/slab.h> 
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/device.h>
#define MMAPIOMEM_DEV_NAME "mmapiomem"  
#define MMAPIOMEM_DEV_MAJOR 280  
#define MMAP_BUF_SIZE 0x500  
char *mmap_buf_ptr;  
int mmapiomem_open(struct inode *inode,struct file *filp)  
{  
    	return 0;  
}  
int mmapiomem_release(struct inode *inode,struct file *filp)  
{  
    	return 0;  
}  
int mmapiomem_mmap(struct file *filp,struct vm_area_struct *vma)  
{  
	int result;  
	unsigned long page;
	vma->vm_flags |= (VM_IO | VM_LOCKED | (VM_DONTEXPAND | VM_DONTDUMP)); 
	vma->vm_flags|=VM_IO;
	page = virt_to_phys(mmap_buf_ptr); 
    	result = remap_pfn_range(vma, vma->vm_start, ((unsigned int)page)>>PAGE_SHIFT, PAGE_SIZE*4, vma->vm_page_prot);  
    	if(result)
	{  
        	return -EAGAIN;  
    	}  
    	return 0;  
}  
struct file_operations mmapiomem_fops={  
	.owner=THIS_MODULE,  
	.open=mmapiomem_open,  
	.release=mmapiomem_release,  
	.mmap=mmapiomem_mmap,  
};  
struct cdev *mmap_cdev;  
struct class *mmap_class;  
int mmapiomem_init(void)  
{  
	int result;  
	int devno = MKDEV(MMAPIOMEM_DEV_MAJOR,0);  
	mmap_buf_ptr = (char*)__get_free_pages(GFP_KERNEL,2);
	printk("%s,the mmap_buf_ptr is 0x%p\n",__func__,mmap_buf_ptr);  
	memset(mmap_buf_ptr,0,PAGE_SIZE*4);   
	*((unsigned int *)(mmap_buf_ptr)) = 0xc3a21b44;
	*((unsigned int *)(mmap_buf_ptr+0x4)) = 0xab8812df; 
	mmap_cdev = cdev_alloc();  
	result = register_chrdev_region(devno,1,"mmap_char_mem");  
	cdev_init(mmap_cdev,&mmapiomem_fops);  
	mmap_cdev->owner = THIS_MODULE;  
	result = cdev_add(mmap_cdev, devno,1);  
	mmap_class = class_create(THIS_MODULE,"mmap_char_class");  
	if (IS_ERR(mmap_class)) {  
		result= PTR_ERR(mmap_class);  
		return -1;  
	}  
    	device_create(mmap_class, NULL, devno, NULL, MMAPIOMEM_DEV_NAME);  
    	return 0;  
}  
void mmapiomem_exit(void)  
{  
	if (mmap_cdev != NULL)  
	cdev_del(mmap_cdev);  
	device_destroy(mmap_class,MKDEV(MMAPIOMEM_DEV_MAJOR,0));  
	class_destroy(mmap_class);  
	unregister_chrdev_region(MKDEV(MMAPIOMEM_DEV_MAJOR,0),1);  
}  
module_init(mmapiomem_init);  
module_exit(mmapiomem_exit);  
MODULE_LICENSE("Dual BSD/GPL");
Makefile:
obj-m := map.o
all:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
以上是内核部分,主要是创建一个字符设备,配置其mmap函数,分配连续内存。分配了4pages,并将首地址开始初始化了8bytes。
具体使用方法是:make insmod map.ko dmesg-c
最后打印出:
mmapiomem_init,the mmap_buf_ptr is 0xffff880129880000
    二、用户态测试
   
user_test.c:
#include <stdio.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <sys/ioctl.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <sys/mman.h>  
#include <linux/vt.h>  
#include <stdint.h> 
#include <stdlib.h>
#define DEVICE_FILENAME "/dev/mmapiomem"  
#define MMAP_SIZE 0x8000  
int main()  
{  
	int ttydev;  
	int dev,i;  
	uint8_t *ptrdata;  
	dev=open(DEVICE_FILENAME,O_RDWR|O_NDELAY);  
    	if(dev>=0)
	{  
		printf("2)open the dev success\n");  
		ptrdata=(uint8_t*)mmap((void*)0x000000000f000000, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dev, 0); 
        	if(ptrdata!=NULL)
		{  
        		printf("the value of ptrdata0 is 0x%02x\n",*ptrdata);
        		printf("the value of ptrdata1 is 0x%02x\n",*(ptrdata+0x1));
        		printf("the value of ptrdata2 is 0x%02x\n",*(ptrdata+0x2));
			printf("the value of ptrdata3 is 0x%02x\n",*(ptrdata+0x3));
			printf("the value of ptrdata4 is 0x%02x\n",*(ptrdata+0x4));
        		printf("the value of ptrdata5 is 0x%02x\n",*(ptrdata+0x5));
			printf("the value of ptrdata6 is 0x%02x\n",*(ptrdata+0x6));
			printf("the value of ptrdata7 is 0x%02x\n",*(ptrdata+0x7));
			printf("the addr_map is: %p\n",ptrdata);  
        	}  	
    	}  
    	close(dev);  
    	printf("6)here close the dev\n");  
    	return 0;  
}   
在虚拟机上测试结果如下:
     
   
可以看到在虚拟机下测试不成功,移步真机得到如下测试结果:
     
   
真机测试成功,可以看到,已经映射成功并且读取了我们之前初始化的值,返回了一个用户空间内存地址。因为程序结束后映射会取消掉,所以如果需要使用该地址的话,直接把这个addr_map传给自己的其他接口函数就行。
 
