您的位置:首页 > 运维架构 > Linux

[arm驱动]linux设备地址映射到用户空间

2014-04-10 22:46 393 查看
一、问题描述:一般情况下,用户空间是不可能也不应该直接访问设备的但是,设备驱动程序中可实现mmap()函数,这个函数可使用户空间直接访问设备的物理地址
1、mmap()函数工作原理:mmap()实现了这样的一个映射过程,它将用户的内存空间的一般内存(准确来说是执行mmap进程的映射区域内存)与设备内存关联,当用户访问用户空间的这段地址范围时,实际上会转化为对设备的访问(linux上一切皆文件)。

文件内存映射原理图示
a



   2、mmap优点:1、对于设备文件,最大的有点就是用户空间可以直接访问设备内存;2、普通文件被映射到进程地址空间后,进程进程访问文件的速度也变块,不必再调read(),write(),可以用memcpy,strcpy等操作写文件,写完后用msync()同步一下。(感觉还是很抽象,看了后面的实例一就明白了)
   3、应用场景:mmap()的这种能力用于显示适配器一类的设备,屏幕帧的像素不再需要从一个用户空间到内核空间的复制过程
二、应用程序相关函数

   1、建立映射:

a) 参数含义:

     addr: 指定映射的起始地址, 通常设为NULL,
由系统指定。

     length: 映射到内存的文件长度。

     prot:   映射区的保护方式, 可以是:

            PROT_EXEC: 映射区可被执行
            PROT_READ: 映射区可被读取

            PROT_WRITE: 映射区可被写入


            PROT_NONE  映射区不可访问.

     flags: 映射区的特性, 可以是:
MAP_SHARED:对此区域所做的修改内容奖写入文件内允许其他映射该文件的进程共享,意思是:n个mmap.out程序在运行,这n个进程的“虚拟内存区域”的物理空间空间都相同。详看《虚拟内存共享原理图b》

虚拟内存共享原理图b





MAP_PRIVATE:对此区域所做的修改不会更改原来的文件内容,对映射区的写入操作会产生一个映射区的复制(copy-on-write);意思是:n个mmap.out程序在运行,但是虚拟内存区域的物理地址内核另外分配。详看《虚拟内存共享原理图c

虚拟内存共享原理图c





     fd: 由open返回的文件描述符, 代表要映射的文件。

     offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射。

b)返回值:返回成功----函数的返回值为最后文件映射到进程空间的地址(参照文件内存映射原理图示
a),进程可直接操作起始地址为该值的有效地址。返回失败返回MAP_FAILED(-1),错误原因存于errno 中。

   2、解除映射:

  3、 同步回写函数:

   如果您希望立即将数据写入文件中,可使用msync。

   a)参数

   start为记忆体开始位置(mmap函数返回的值---地址),length为长度。

   flags则有三个:

   MS_ASYNC : 请Kernel快将资料写入,发出回写请求后立即返回

   MS_SYNC : 在msync结束返回前,将资料写入。
MS_INVALIDATE使用回写的内容更新该文件的其它映射

mmap普通文件被映射到进程地址空间实例

   mmapfile.c

   Tip:此时mapchar就是虚拟内存区域的物理地址部分的首地址;也就是《文件内存映射原理图示
a》中的fd文件存储映射部分对应的的首地址,[b]当进车访问[b]mapchar这段地址范围时,实际上会转化为对文件fd的访问[/b][/b]

   运行结果:

   Tip:一个进程的内存映象由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存映射的区域。一个进程的内存区域可以通过查看/proc/pid/maps

三、给驱动设备添加mmap虚拟内存映射


  1、 驱动中的mmap(内核空间):

2、在struct file_operations中与mmap接口函数关联

       3、struct vm_area_struct(VMA)结构体如下

   4、struct vm_area_struct(VMA)结构体flag参数

   VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump )中

   VM_RESERVED标志内存区域不能被换出

   5、内核mmap中创建页表函数:remap_pfn_range();

          作用用“addr ~ addr + size之间的虚拟地址”构造页表,参考《虚拟内存共享原理图b》和《虚拟内存共享原理图c

   a)参数

        1) vma:           虚拟内存区域指针(默认使用vma)

        2)addr:   虚拟地址的起始值(默认使用vma->vm_start)

        3)pfn:总的来说(pfn = virt_to_phys(void *mem))>>PAGE_SHIFT(常用);或使用默认方式vma->vm_pgoff)

            推理:pfn是虚拟地址应该映射到的物理地址所在的物理页帧号就是物理地址右移PAGE_SHIFT位,若PAGE_SIZE为4k则PAGE_SHIFT为12(2的12次方为4k),因此PAGE_SIZE为1<<PAGE_SHIFT;虚拟地址可将"物理地址>>PAGE_SHIFT"得到(虚拟地址
= 物理地址>>PAGE_SHIFT
)。如何得到物理地址:将驱动设备中某个内存变量用函数virt_to_phys(void *mem)转换成物理地址;(物理地址 = virt_to_phys(void *mem));

        4)size:           要映射的区域的大小。(默认使用vma->vm_end - vma->vm_start)

        5)prot:            VMA的保护属性。(默认使用vma->vm_page_prot)

   6、mmap驱动模板

   file_oprations中添加或修改.mmap

7、完整实例

   a)驱动部分

   b)与驱动对应的应用程序部分

第三部分:struct stat 作用

     1、stat,lstat,fstat1 函数都是获取文件(普通文件,目录,管道,socket,字符,块()的属性。函数原型#include <sys/stat.h>

   2、向stat,fstat1、lstat传入文件名字path、fd、path,获取文件对应属性buf。

  3、 struct stat结构

、与mmap应用程序中“普通文件虚拟内存映射模板和实例

1、 [b]mmap()应用程序模板[/b]

2、完整实例

   Tip:mmap回写时,回写字节最大大小为fileStat.st_size,所以定义字节大fileStat.st_size。(这个我没有根据,只是实验结果是这样)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: