mmap映射物理内存到用户空间的方法

在内核申请一片物理内存,映射到用户空间使用的方法。环境:Linux ubuntu 4.10.0-42-generic。

#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_RESERVED|VM_SHARED; 
    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
效果:

[10083.180080] mmapiomem_init,the mmap_buf_ptr is 0xffff9cc619c14000

以上是内核部分,主要是创建一个字符设备,配置其mmap函数,分配连续内存。分配了4pages,并将首地址开始初始化了8bytes。

具体使用方法是:make    insmod map.ko    dmesg-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;  
    int loop,loop2;  
    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;  
}   

效果:

2)open the dev success
the value of ptrdata0 is 0x44
the value of ptrdata1 is 0x1b
the value of ptrdata2 is 0xa2
the value of ptrdata3 is 0xc3
the value of ptrdata4 is 0xdf
the value of ptrdata5 is 0x12
the value of ptrdata6 is 0x88
the value of ptrdata7 is 0xab
the addr_map is: 0xf000000

可以看到,已经映射成功并且读取了我们之前初始化的值,返回了一个用户空间内存地址。因为程序结束后映射会取消掉,所以如果需要使用该地址的话,直接把这个addr_map传给自己的其他接口函数就行。

感谢博主:

tmkeepgood

展开阅读全文

没有更多推荐了,返回首页