内存映射:mmap与ioremap

对于提供了MMU(存储管理器,辅助操做系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux提供了复杂的存储管理系统,使得进程所能访问的内存达到4GB。

  进程的4GB内存空间被人为的分为两个部分--用户空间与内核空间。用户空间地址分布从0到3GB(PAGE_OFFSET,在0x86中它等于0xC0000000),3GB到4GB为内核空间。

  1.无论是在用户空间仍是在内核空间,软件一概不能去直接访问设备的物理地址;
  2.在内核驱动中若是要访问设备的物理地址,须要利用ioremap将设备的物理地址映射到内核虚拟地址上(动态内存映射区),之后驱动程序访问这个内核虚拟地址就是在间接得访问设备的物理地址(MMU,TLB,TTW)
  3.若是用户要访问硬件设备,不能直接访问,也不能在用户空间访问,只能经过系统调用(open,close,read,write,ioctl)来访问映射好的内核虚拟地址,经过这种间接的访问来访问硬件设备,可是若是涉及到数据的拷贝,还须要借助4个内存拷贝函数!

  1.应用程序经过read,write,ioctl来访问硬件设备,它们都要通过两次的数据拷贝,一次是用户空间和内核空间的数据拷贝,另一次是内核空间和硬件之间的数据拷贝,若是设备拷贝的数据量比较小,那么read,write,ioctl的两次数据拷贝的过程对系统的影响几乎能够忽略不计,若是设备的数据量很是大,例如显卡(独立),LCD屏幕(显存共享主存),摄像头,声卡这类设备涉及的数据量比较庞大,若是仍是用read,write,ioctl进行访问设备数据,无形对系统的性能影响很是大。
  2.用户访问设备,最终其实涉及的用户和硬件,而read,write,ioctl自己会牵扯到内核,因此这些函数涉及2次的数据拷贝,用户要直接去访问硬件设备,只须要将硬件设备的物理地址信息映射到用户的虚拟地址空间便可,一旦完毕,不会在牵扯到内核空间,之后用户直接访问用户的虚拟地址就是在访问设备硬件,由2次的数据拷贝的转换为一次的数据拷贝。未使用mmap,应用程序只能经过系统调用(read,write,ioctl)内核函数,内核函数得到硬件数据,应用程序再从内核函数获取数据;使用mmap后,用户程序能够直接访问映射后的虚拟地址得到硬件数据。

mmap:将硬件物理地址映射到用户虚拟地址空间,应用程序能够直接访问映射后的地址。由2次数据拷贝变成1次数据拷贝!mmap用于在用户程序中映射整个设备的物理地址。
ioremap:是将物理地址转换为内核虚拟地址,内核能够直接访问映射后的地址。ioremap用于在内核程序中映射设备的一个物理寄存器。


mmap是将设备内存线性地址映射到用户地址空间,用于大片数据好比音视频存储空间。
最后,咱们要特别强调驱动程序中mmap函数的实现方法。用mmap映射一个设备,意味着使用户空间的一段地址关联到设备内存上,这使得只要程序在分配的地址范围内进行读取或者写入,实际上就是对设备的访问。

linux的线程只能访问虚拟地址,无论是否是内核,ioremap应用,好比有个寄存器地址是0xe8000000你要用ioremap映射后,才能访问地址0xe8000000。这两个地址是不一样的,mmu会帮你搞定,对你是透明的

一、ioremap函数定义  linux

 ioremap宏定义在asm/io.h内:

#define ioremap(cookie,size)           __ioremap(cookie,size,0)

__ioremap函数原型为(arm/mm/ioremap.c):

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);

参数:

phys_addr:要映射的起始的IO地址

size:要映射的空间的大小

flags:要映射的IO空间和权限有关的标志

该函数返回映射后的内核虚拟地址(3G-4G). 接着即可以经过读写该返回的内核虚拟地址去访问之这段I/O内存资源。

文章目录
1.1 背景
1.1.1 ARM32 内存空间
1.1.2 ioremap 实现
1.1.3 Linux内存属性
上篇文章:ARM Linux 内存管理入门及渐进 4 - 常用接口实现(memcpy/copy_to_user)

1.1 背景
在编写 linux 驱动过程中,不可避免的会涉及操作外设,而外设的地址空间与 DDR的地址空间一般不连续,在 linux上电时,并不会为外设地址空间建立页表,又因为linux 访问内存使用的都是虚拟地址,因此如果想访问外设的寄存器(一般包括数据寄存器、控制寄存器与状态寄存器),需要在驱动初始化中将外设所处的物理地址映射为虚拟地址,linux 为应对该问题提供了较多接口以应对不同的场景需求,arch/arm64(arm)/include/asm/io.h中有如下几种 ioremap 接口:

ioremap
ioremap_wc
devm_ioremap
devm_ioremap_resource
1
2
3
4
ioremap:用来映射 memory type 为 device memory 的设备,同时不使用cache(device memory本身就没有 cacheable 这个属性),即 CPU 的读写操作直接操作设备内存。
ioremap_nocache:的实现与 ioremap 完全相同,保留该符号是因为向后兼容使用ioremap_nocache接口的驱动程序。
ioremap_cached 用来映射 memory type 为 normal memory 的设备,同时使用cache,这会提高内存的访问速度,提高系统的性能。
ARM Linux 引入设备树特性后,一些支持设备树的设备驱动不再使用直接 ioremap(),改用 drivers/of/address.c/of_iomap(),of_iomap() 的内部仍然会调用 ioremap()

1.1.1 ARM32 内存空间
通常进程的4GB内存空间被人为的分为两个部分:

用户空间与, 用户空间地址分布从0到3GB;
内核空间,3GB-4GB。
内核空间中,从3G到 vmalloc_start 这段地址是物理内存映射区域(该区域中包含了内核镜像、物理页框表mem_map等等)。在物理内存映射区之后,就是vmalloc区域,vmalloc_end 的位置接近4G(最后位置系统会保留一片128k大小的区域用于专用页面映射)。
1.1.2 ioremap 实现
arch/arm64/include/asm/io.h

#define ioremap(addr, size)             __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define ioremap_nocache(addr, size)     __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define ioremap_wc(addr, size)          __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
#define ioremap_wt(addr, size)          __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define iounmap                         __iounmap
1
2
3
4
5
从上面的接口定义看,除了addr和size外还有一个__pgprot()参数,上面接口中参数类型有:

PROT_DEVICE_nGnRE;
PROT_NORMAL_NC。
两种类型。实际在 pgtable-prot.h 中还有 PROT_NORMAL_WT和PROT_NORMAL。ioremap_xxx 接口的功能属性都是由__pgprot()决定的,那__pgprot()具体是什么含义呢?
1.1.3 Linux内存属性
在内核源码memory.h中有定义ioremap接口中可用的内存类型:

arch/arm64/include/asm/memory.h

/*
 * Memory types available.
 */
#define MT_DEVICE_nGnRnE        0
#define MT_DEVICE_nGnRE         1
#define MT_DEVICE_GRE           2
#define MT_NORMAL_NC            3
#define MT_NORMAL               4
#define MT_NORMAL_WT            5

/*
 * Memory types for Stage-2 translation
 */
#define MT_S2_NORMAL            0xf
#define MT_S2_DEVICE_nGnRE      0x1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上面 ioremap_xxx 接口中用到的属性参数主要是PROT_DEVICE_nGnRE和PROT_NORMAL_NC。看下这两个prot_val分别代表什么含义:

#define PROT_DEFAULT            (_PROT_DEFAULT | PTE_MAYBE_NG)
#define PROT_SECT_DEFAULT       (_PROT_SECT_DEFAULT | PMD_MAYBE_NG)

#define PROT_DEVICE_nGnRnE      (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))
#define PROT_DEVICE_nGnRE       (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_NORMAL_NC          (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
#define PROT_NORMAL_WT          (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
#define PROT_NORMAL             (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))

#define PROT_SECT_DEVICE_nGnRE  (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_SECT_NORMAL        (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
#define PROT_SECT_NORMAL_EXEC   (PROT_SECT_DEFAULT | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
1
2
3
4
5
6
7
8
9
10
11
12
参照ARMv8手册中对内存属性的描述,内存可以分为DEVICE和NORMAL两大类型以及Device memory依据是否可合并等属性。

Normal型:sram或者dram那样的内存空间,一般都是过cache的(当然也可不过cache,如外设访问的地址空间,标记为NC)
Device型:设备寄存器那样的io空间,都不会过cache。
Device属性的内存空间还有下面三种子属性,都有打开和关闭的定义。

G(gather:对多个memory的访问可以合并) nG与之相反;
R(Reordering:对内存访问指令进行重排) nR与之相反;
E(Early Write Acknowledgement hint:写操作的ack可提早应答) nE与之相反。
MAIR寄存器定义如下:
Linux 预先定义了6种内存属性,分别存在MAIR寄存器的attr0~attr5。内存页表属性部分可以选择这个寄存器的某个index,范围(0~5)作为自己的属性。

上篇文章:ARM Linux 内存管理入门及渐进 4 - 常用接口实现(memcpy/copy_to_user)

推荐阅读:
https://blog.csdn.net/m0_51717456/article/details/124259339
https://blog.csdn.net/suifengershi2000/article/details/122805722
————————————————
版权声明:本文为CSDN博主「CodingCos」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_32960911/article/details/127969010

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值