驱动学习笔记9用户空间地址映射mmap,应用程序直接用指针操作寄存器-创新互联

3.3.mmap地址映射方法 

  明确:mmap就是完成物理地址映射到用户虚拟地址用的
 明确:用户3G虚拟地址空间划分:
       0x00000000-------------------------------------------------------0xBFFFFFFF
                 代码段  数据段  BSS段  堆区   MMAP虚拟内存区      栈区
                                        ----><------            <----

成都创新互联是一家集网站建设,清镇企业网站建设,清镇品牌网站建设,网站定制,清镇网站建设报价,网络营销,网络优化,清镇网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
1.回顾mmap系统调用函数

函数原型: 
 void *mmap(void *addr, size_t length, 
          int prot, int flags,
int fd, off_t offset);
 函数功能:将物理地址空间映射到用户虚拟内存空间上
           将物理地址映射到用户虚拟地址
           老王课程这么讲:将文件映射到用户虚拟地址空间上
           文件对应硬件外设,外设通过物理地址访问
           linux系统不允许访问物理地址,利用mmap映射不得了
 参数:
  addr:给NULL,让linux内核帮你在用户虚拟内存区域找一块
      空间内存用来映射物理地址
 length:让linux内核帮你找的空闲用户虚拟内存的大小
         切记:大小必须是页面大小(4KB)的整数倍
 prot:描述内核帮你找的空闲用户虚拟内存的访问权限
      一般指定为:PROT_READ|PROT_WRITE
  flags:其余属性,一般指定为:MAP_SHARED
  fd:硬件外设
 offset:偏移量,一般给0
 返回值:linux内核将空闲的用户虚拟内存的首地址进行返回
         这个起始用户虚拟地址同样也是4KB整数倍
 
 参考代码:
 void *addr;
 int fd = open("a.txt", O_RDWR);
 addr = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, 
             MAP_SHARED, fd, 0);
 说明:将文件a.txt映射到以addr起始的用户虚拟内存上
       将来访问映射的用户虚拟内存就是访问文件
 
 //向映射的用户虚拟内存拷贝字符串数据
本质是向文件a.txt写入数据
 memcpy(addr, "hello,world", 12);

2.了解mmap系统调用函数所做的工作:

2.1.应用程序调用mmap,首先跑到C库的mmap函数定义
2.2.C库的mmap函数作两件事:
1.保存mmap系统调用号到r7寄存器
2.调用swi/svc指令触发软中断异常
2.3.一旦触发软中断异常,CPU核立马处理软中断异常
2.4.最后进程跑到内核空间继续运行,跑到软中断异常的入口地址
运行,做如下事情:
1.调用软中断异常处理函数,而此函数做两件事:
 1.从r7寄存器中取出mmap系统调用号
     2.以mmap系统调用号为下标在内核的系统调用表中
       找到mmap对应的内核函数sys_mmap,而内核的
       sys_mmap做三件事:
       1.内核的sys_mmap首先在当前进程的3G虚拟地址空间中
         找一块空闲的用户虚拟内存,将来用于和物理地址
         做映射
       2.一旦找到空闲的用户虚拟内存,并且用户mmap本身
         也给用户虚拟内存指定了一堆的属性(大小,权限等)
         所以内核用struct vm_area_struct数据结构定义
         初始化一个对象来描述空闲的用户虚拟内存的属性
         struct vm_area_struct {
             unsigned long vm_start; //空间用户虚拟内存的起始地址
                                     //等于mmap的返回值addr
             unsigned long vm_end; //结束地址=vm_start+大小 
             pgprot_t vm_page_prot; //等于mmap传递的PROT_READ|PROT_READ|PROT_WRITE
             unsigned long vm_flags;    //等于mmap传递的MAP_SHARED
             unsigned long vm_pgoff;     //等于mmap传递的0
             ...
         };
       3.最后内核的sys_mmap调用底层驱动的mmap接口
  并且内核sys_mmap将第2步创建的对象的地址
  也传递给底层驱动的mmap接口
         
3.底层驱动mmap执行完毕,进程返回,至此mmap调用结束   


  
3.对应的底层驱动的mmap接口 

struct file_operations {
     int (*mmap) (struct file *file, 
                 struct vm_area_struct *vma);
};
接口功能:永远只能唯一做一件事:将已知的物理地址和已知的
         用户虚拟地址做映射,类似:媒婆
         由于用户虚拟地址在用户空间,所以将来访问操作
         都是在应用程序完成,而不是在内核驱动完成
         访问映射的用户虚拟地址就是在访问物理地址
file:跟fd亲戚关系
vma:指向内核sys_mmap创建的一个对象,此对象来描述空闲的
用户虚拟内存的各种属性,将来底层驱动mmap接口利用
此指针可以获取到用户虚拟内存的属性:
vma->vm_start //获取起始用户虚拟地址
vma->vm_end 
vma->vm_flags
vma->vm_page_prot
...
问:底层驱动的mmap接口到底如何最终完成映射呢?
因为已知物理地址可以看手册获取到
已知的用户虚拟地址通过vma指针能够获取到
如何将两者关联在一起呢?
万事俱备只欠东风
答:只需调用以下函数完成关联映射:
int remap_pfn_range(struct vm_area_struct *vma, 
                     unsigned long addr,
                     unsigned long pfn, 
                     unsigned long size, 
                     pgprot_t prot);
函数功能:完成最终的地址映射
vma:传递内核sys_mmap创建的对象地址
       也就是传递驱动mmap接口的第二个参数
addr:传递空闲的用户虚拟内存的首地址
也就是传递vma->vm_start
pfn:传递起始的物理地址>>12
        切记:此物理地址大小必须是4KB(0x1000)整数倍
        例如:
             0xC001C000>>12:合法
             0xC001C004>>12:不合法
size:传递映射的用户虚拟内存的大小
  也就是传递:vma->vm_end - vma->vm_start 
prot:传递用户虚拟内存的访问权限
         也就是传递:vma->vm_page_prot

案例:利用mmap实现开关灯操作
参考代码:day09/1.0

4.世纪大PK:read,write,ioctl和mmap对比 

什么时候用:read,write,ioctl
什么时候用: mmap
4.1.read,write,ioctl数据操作流程
  对设备读操作:read,ioctl
     数据流:硬件寄存器----->内核缓冲区------->用户缓冲区
                   gpio_get_value     copy_to_user
 对设备写操作:write,ioctl
     数据流:用户缓冲区----->内核缓冲区------->硬件寄存器 
                     copy_from_user    gpio_set_value
                     
 结论:read,write,ioctl数据操作势必要经过两次数据拷贝:
用户-内核->硬件
硬件->内核->用户 

4.2.mmap数据操作流程:
  对设备读操作: 应用程序直接以指针形式读取寄存器
                data = *gpiocout;
  对设备写操作: 应用程序直接以指针的形式写入寄存器    
                *gpiocout &= ~(1<< 12);
 结论:mmap数据操作只需一个数据拷贝:
       用户->硬件
       硬件->用户
       
4.3.终极结论:
  1.如果用户对硬件操作访问的数据量比较小,read,write,ioctl
的两次数据拷贝对系统性能肯定有影响,但是这种影响几乎
可以忽略不计,如果操作的数据量比较大,两次数据拷贝
性能的影响是致命的,例如:摄像头,LCD显示屏,声卡等
 2.如果访问操作的数据量比较大,用read,write,ioctl势必
影响系统的性能,务必采用mmap,将两次数据拷贝变成一次
提供系统的性能效率
 3.由于mmap在使用的时候,分配的用户虚拟内存必须是4KB的
整数倍,如果操作的数据量比较小,此时还用mmap
即使提高了系统的性能(几乎体会不到),反而是浪费了
宝贵的内存资源

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧

网页题目:驱动学习笔记9用户空间地址映射mmap,应用程序直接用指针操作寄存器-创新互联
当前URL:https://www.cdcxhl.com/article2/ceggic.html

成都网站建设公司_创新互联,为您提供关键词优化网站设计公司标签优化服务器托管网站内链搜索引擎优化

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都定制网站建设