mmap相关_20110818

 

内存映射
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)

内存映射函数mmap负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read,write等操作。
addr:指定映射的起始地址,通常设为NULL,由系统指定。
length:映射到内存的文件长度。
prot:映射的保护方式,可以是:
PROT_EXEC:映射区可被执行
PROT_READ:映射区可被读取
PROT_WRITE:映射区可被写入
PROT_NONE:映射区不能存取
Flags:映射区的特性,可以是:
MAP_SHARED:
写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享。
MAP_PRIVATE:
对映射区的写入操作会产生一个映射区的复制(copy_on_write),对此区域所做的修改不会写回原文件。
fd:由open返回的文件描述符,代表要映射的文件。
offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射。

解除映射:
int munmap(void *start, size_t length)

功能:取消参数start所指向的映射内存,参数length表示欲取消的内存大小。
返回值:解除成功返回0,否则返回-1,错误原因存在于errno中。

虚拟地址区域:vm_area_struct
Linux内核使用结构vm_area_struct(<linux/mm_types.h>)描述虚拟内存区域,其中几个主要成员如下:
unsigned long vm_start 虚拟内存区域起始地址
unsigned long vm_end   虚拟内存区域结束地址
unsigned long vm_flags 该区域的标志
如:VM_IO和VM_RESERVED。VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump)中,VM_RESERVED标志内存区域不能被换出。

mmap设备操作
映射一个设备是指把用户空间的一段地址关联到设备内存上,当程序读写这段用户空间的地址时,它实际上是在访问设备。这里需要做的两个操作:
1.找到可以用来关联的虚拟地址区间
2.关联
其中找到可以用来关联的虚拟地址区间是由内核完成的,mmap只要关联这个操作。
mmap方法是file_operations结构的成员,在mmap系统调用发出时被调用。在此之前,内核已经完成了很多工作。mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表。

void (*mmap)(struct file*, struct vm_area_struct *)

其中第二个参数struct vm_area_struct *相当于内核找到的,可以拿来用的虚拟内存区间。


mmap完成页表的建立:
方法有二:
1.使用remap_pfn_range一次建立所有页表;
2.使用nopage VMA方法每次建立一个页表;
构造页表的工作可由remap_pgn_range函数完成,原型如下:

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot)

vma是内核为我们找到的虚拟地址空间,addr要关联的是虚拟地址,pfn是要关联的物理地址,size是关联的长度是多少。
mmap设备操作实例:

1.    int memdev_map(struct file *filp, struct vm_area_struct *vma){  

2.        vma->vm_flags |= VM_IO;  

3.        vma->vm_flags |= VM_RESERVED;  

4.       if(remap_pgn_range(vma,vma->start, virt_to_phys(dev->data>>PAGE_SHIFT),size,vma->vm_page_prot))  

5.            return -EAGAIN;  

6.        return 0;  

7.    }  

先说一下对于ARM而言虚拟地址与物理地址的关系:
在arch/arm/include/asm/memory.h中:

1. #define __virt_to_phys(x)   ((x) - PAGE_OFFSET + PHYS_OFFSET)  

2. #define __phys_to_virt(x)   ((x) - PHYS_OFFSET + PAGE_OFFSET)   

3.   

4.    static inline unsigned long virt_to_phys(void *x)  

5.    {  

6.       return __virt_to_phys((unsigned long)(x));  

7.   }  

8.     

9.    static inline void *phys_to_virt(unsigned long x)  

10.  {  

11.      return (void *)(__phys_to_virt((unsigned long)(x)));  

12.  }  

上面转换过程的PAGE_OFFSET通常为3G,而PHYS_OFFSET则定于为系统DRAM内存的基地址。因此,对于我们的开发板,并不是将0地址映射到3G,而是将外接的SDRAM的首地址映射到3G。注意:这里的virt_to_phys和phys_to_virt方法仅适用于896MB以下的低端内存,高端内存的虚拟地址与物理地址之间不存在如此简单的换算关系。下边是fbmem.c中的mmap操作,示意图如下:

1. static int  

2.    fb_mmap(struct file *file, struct vm_area_struct * vma)  

3.   __acquires(&info->lock)  

4.    __releases(&info->lock)  

5.    {  

6.        int fbidx = iminor(file->f_path.dentry->d_inode);  

7.        struct fb_info *info = registered_fb[fbidx];  

8.        struct fb_ops *fb = info->fbops;  

9.        unsigned long off;  

10.      unsigned long start;  

11.      u32 len;  

12.    

13.      if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))  

14.          return -EINVAL;  

15.      /* 

16.       *vma->vm_pgoff是vma区域在文件中的偏移量,即图中的off,左移PAGE_SHIFT是把以页为单位转为以字节为单位 

17.       *若PAGE_SIZE为4KB,则PAGE_SHIFT为12,因为PAGE_SIZE等于1<<PAGE_SHIFT。 

18.       */  

19.      off = vma->vm_pgoff << PAGE_SHIFT;  

20.      if (!fb)  

21.          return -ENODEV;  

22.      /*如果具体设备驱动中实现了mmap,则调用具体设备驱动中的mmap,否则使用fbmem.c中的*/  

23.      if (fb->fb_mmap) {  

24.          int res;  

25.          mutex_lock(&info->lock);  

26.          res = fb->fb_mmap(info, vma);  

27.          mutex_unlock(&info->lock);  

28.          return res;  

29.      }  

30.    

31.      mutex_lock(&info->lock);  

32.    

33.      /*显存的物理地址*/  

34.      start = info->fix.smem_start;  

35.      /*求出帧缓冲的长度*/  

36.      len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);  

37.      if (off >= len) {  

38.          /* memory mapped io */  

39.          off -= len;  

40.          if (info->var.accel_flags) {  

41.              mutex_unlock(&info->lock);  

42.              return -EINVAL;  

43.          }  

44.          /*内存映射I/O的开始位置*/  

45.          start = info->fix.mmio_start;  

46.          /*内存映射I/O的长度*/    

47.          len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);  

48.      }  

49.      mutex_unlock(&info->lock);  

50.      /*把起始地址页对齐*/  

51.      start &= PAGE_MASK;  

52.      /*如果区域大于总长度,报错*/  

53.      if ((vma->vm_end - vma->vm_start + off) > len)  

54.          return -EINVAL;  

55.      /*得到在文件中的偏移*/  

56.      off += start;  

57.      /*把以页为单位转为以字节为单位*/  

58.      vma->vm_pgoff = off >> PAGE_SHIFT;  

59.      /* This is an IO map - tell maydump to skip this VMA */  

60.      /*设置VMA标志,将VMA设置成一个内存映射的IO区域,VM_RESERVED告诉内存管理系统不要将VMA交换出去*/  

61.      vma->vm_flags |= VM_IO | VM_RESERVED;  

62.      fb_pgprotect(file, vma, off);  

63.      /*真正建立映射的部分,为物理地址和虚拟地址建立页表*/  

64.      if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,  

65.                   vma->vm_end - vma->vm_start, vma->vm_page_prot))  

66.          return -EAGAIN;  

67.      return 0;  

68.  }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值