Linux设备驱动之mmap设备操作

1.mmap系统调用 
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
功能:负责把文件内容映射到进程的虚拟地址空间,通过对这段内存的读取和修改来实现对文件的读取和修改,而不需要再调用read和write;
参数:addr:映射的起始地址,设为NULL由系统指定;
len映射到内存的文件长度;
prot期望的内存保护标志,不能与文件的打开模式冲突。PROT_EXEC,PROT_READ,PROT_WRITE等;
flags:指定映射对象的类型,映射选项和映射页是否可以共享。MAP_SHARED,MAP_PRIVATE等;
fd:由open返回的文件描述符,代表要映射的文件;
offset:开始映射的文件的偏移。
返回值:成功执行时,mmap()返回被映射区的指针。失败时,mmap()返回MAP_FAILED。

 mmap映射图:

 

2.解除映射:
 int munmap(void *start, size_t length);
3.虚拟内存区域:
虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。一个进程的内存映象由下面几个部分组成:程序代码、数据、BSS和栈区域,以及内存映射的区域。
linux内核使用vm_area_struct结构来描述虚拟内存区。其主要成员:
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address within vm_mm. */
unsigned long vm_flags; /* Flags, see mm.h. 该区域的标记。如VM_IO(该VMA标记为内存映射的IO区域,会阻止系统将该区域包含在进程的存放转存中)和VM_RESERVED(标志内存区域不能被换出)。*
4.mmap设备操作:
映射一个设备是指把用户空间的一段地址(虚拟地址区间)关联到设备内存上,当程序读写这段用户空间的地址时,它实际上是在访问设备。
mmap方法是file_operations结构的成员,在mmap系统调用的发出时被调用。在此之前,内核已经完成了很多工作。
mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表(虚拟地址和设备的物理地址的关联通过页表)。
static int mmap(struct file *file, struct vm_area_struct *vma);
mmap如何完成页表的建立?(两种方法)
(1)使用remap_pfn_range一次建立所有页表。
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot);
/**
* remap_pfn_range - remap kernel memory to userspace
* @vma: user vma to map to:内核找到的虚拟地址区间
* @addr: target user address to start at:要关联的虚拟地址
* @pfn: physical address of kernel memory:要关联的设备的物理地址,也即要映射的物理地址所在的物理帧号,可将物理地址>>PAGE_SHIFT
* @size: size of map area
* @prot: page protection flags for this mapping
*
* Note: this is only safe if the mm semaphore is held when called.
*/
(2)使用nopage VMA方法每次建立一个页表;
5.源码分析
(1)memdev.h
  1. #ifndef _MEMDEV_H_  
  2. #define _MEMDEV_H_  
  3.   
  4. #ifndef MEMDEV_MAJOR  
  5. #define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/  
  6. #endif  
  7.   
  8. #ifndef MEMDEV_NR_DEVS  
  9. #define MEMDEV_NR_DEVS 2    /*设备数*/  
  10. #endif  
  11.   
  12. #ifndef MEMDEV_SIZE  
  13. #define MEMDEV_SIZE 4096  
  14. #endif  
  15.   
  16.   
  17. /*mem设备描述结构体*/  
  18. struct mem_dev                                       
  19. {                                                          
  20.   char *data;                        
  21.   unsigned long size;         
  22. };  
  23.   
  24. #endif /* _MEMDEV_H_ */ 

 (2)memdev.c

  1. #include <linux/module.h>  
  2. #include <linux/types.h>  
  3. #include <linux/fs.h>  
  4. #include <linux/errno.h>  
  5. #include <linux/mm.h>  
  6. #include <linux/sched.h>  
  7. #include <linux/init.h>  
  8. #include <linux/cdev.h>  
  9. #include <asm/io.h>  
  10. #include <asm/system.h>  
  11. #include <asm/uaccess.h>  
  12.   
  13. #include <linux/kernel.h>  
  14. #include "memdev.h"  
  15.   
  16.   
  17. static int mem_major = MEMDEV_MAJOR;  
  18.   
  19. module_param(mem_major, int, S_IRUGO);  
  20.   
  21. struct mem_dev *mem_devp; /*设备结构体指针*/  
  22.   
  23. struct cdev cdev;   
  24.   
  25. /*文件打开函数*/  
  26. int mem_open(struct inode *inode, struct file *filp)  
  27. {  
  28.     struct mem_dev *dev;  
  29.       
  30.     /*获取次设备号*/  
  31.     int num = MINOR(inode->i_rdev);  
  32.   
  33.     if (num >= MEMDEV_NR_DEVS)   
  34.             return -ENODEV;  
  35.     dev = &mem_devp[num];  
  36.       
  37.     /*将设备描述结构指针赋值给文件私有数据指针*/  
  38.     filp->private_data = dev;  
  39.       
  40.     return 0;   
  41. }  
  42.   
  43. /*文件释放函数*/  
  44. int mem_release(struct inode *inode, struct file *filp)  
  45. {  
  46.   return 0;  
  47. }  
  48. static int memdev_mmap(struct file*filp, struct vm_area_struct *vma)  
  49. {  
  50.       struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/  
  51.         
  52.       vma->vm_flags |= VM_IO;  
  53.       vma->vm_flags |= VM_RESERVED;  
  54.   
  55.        
  56.       if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))  
  57.           return  -EAGAIN;  
  58.                   
  59.       return 0;  
  60. }  
  61.   
  62. /*文件操作结构体*/  
  63. static const struct file_operations mem_fops =  
  64. {  
  65.   .owner = THIS_MODULE,  
  66.   .open = mem_open,  
  67.   .release = mem_release,  
  68.   .mmap = memdev_mmap,  
  69. };  
  70.   
  71. /*设备驱动模块加载函数*/  
  72. static int memdev_init(void)  
  73. {  
  74.   int result;  
  75.   int i;  
  76.   
  77.   dev_t devno = MKDEV(mem_major, 0);  
  78.   
  79.   /* 静态申请设备号*/  
  80.   if (mem_major)  
  81.     result = register_chrdev_region(devno, 2, "memdev");  
  82.   else  /* 动态分配设备号 */  
  83.   {  
  84.     result = alloc_chrdev_region(&devno, 0, 2, "memdev");  
  85.     mem_major = MAJOR(devno);  
  86.   }    
  87.     
  88.   if (result < 0)  
  89.     return result;  
  90.   
  91.   /*初始化cdev结构*/  
  92.   cdev_init(&cdev, &mem_fops);  
  93.   cdev.owner = THIS_MODULE;  
  94.   cdev.ops = &mem_fops;  
  95.     
  96.   /* 注册字符设备 */  
  97.   cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);  
  98.      
  99.   /* 为设备描述结构分配内存*/  
  100.   mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);  
  101.   if (!mem_devp)    /*申请失败*/  
  102.   {  
  103.     result =  - ENOMEM;  
  104.     goto fail_malloc;  
  105.   }  
  106.   memset(mem_devp, 0, sizeof(struct mem_dev));  
  107.     
  108.   /*为设备分配内存*/  
  109.   for (i=0; i < MEMDEV_NR_DEVS; i++)   
  110.   {  
  111.         mem_devp[i].size = MEMDEV_SIZE;  
  112.         mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);  
  113.         memset(mem_devp[i].data, 0, MEMDEV_SIZE);  
  114.   }  
  115.       
  116.   return 0;  
  117.   
  118.   fail_malloc:   
  119.   unregister_chrdev_region(devno, 1);  
  120.     
  121.   return result;  
  122. }  
  123.   
  124. /*模块卸载函数*/  
  125. static void memdev_exit(void)  
  126. {  
  127.   cdev_del(&cdev);   /*注销设备*/  
  128.   kfree(mem_devp);     /*释放设备结构体内存*/  
  129.   unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/  
  130. }  
  131.   
  132. MODULE_AUTHOR("David Xie");  
  133. MODULE_LICENSE("GPL");  
  134.   
  135. module_init(memdev_init);  
  136. module_exit(memdev_exit); 

 (3)app-mmap.c

  1. #include <stdio.h>  
  2. #include<sys/types.h>  
  3. #include<sys/stat.h>  
  4. #include<fcntl.h>  
  5. #include<unistd.h>  
  6. #include<sys/mman.h>  
  7.   
  8. int main()  
  9. {  
  10.     int fd;  
  11.     char *start;  
  12.     //char buf[100];  
  13.     char *buf;  
  14.       
  15.     /*打开文件*/  
  16.     fd = open("/dev/memdev0",O_RDWR);  
  17.           
  18.     buf = (char *)malloc(100);  
  19.     memset(buf, 0, 100);  
  20.     start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  
  21.       
  22.     /* 读出数据 */  
  23.     strcpy(buf,start);  
  24.     sleep (1);  
  25.     printf("buf 1 = %s\n",buf);      
  26.   
  27.     /* 写入数据 */  
  28.     strcpy(start,"Buf Is Not Null!");  
  29.       
  30.     memset(buf, 0, 100);  
  31.     strcpy(buf,start);  
  32.     sleep (1);  
  33.     printf("buf 2 = %s\n",buf);  
  34.   
  35.          
  36.     munmap(start,100); /*解除映射*/  
  37.     free(buf);  
  38.     close(fd);    
  39.     return 0;      

 

posted @ 2015-01-10 13:59  来杯绿茶  阅读(370)  评论(0编辑  收藏  举报