内存属性

552 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

ARMv8架构处理器主要提供两种类型的内存属性,分别是普通(normal)内存和设备(device)内存。

普通内存

普通内存是弱一致性的(weakly ordered),没有额外的约束,可以提供最高的内存访问性能。通常代码段、数据段以及其他数据都会放在普通内存中。普通内存可以让处理器做很多的优化,如分支预测、数据预取、高速缓存行预取和填充、乱序加载等硬件优化。

设备内存

处理器访问设备内存会有很多限制,如不能进行预测访问等。设备内存是严格按照指令顺序来执行的。通常设备内存留给设备来访问。若系统中所有内存都设置为设备内存,就会有很大的副作用。ARMv8架构定义了多种设备内存的属性:

  • Device-nGnRnE;
  • Device-nGnRE;
  • Device-nGRE;
  • Device-GRE。

Device后的字母是有特殊含义的。 G和nG:分别表示聚合(Gathering)与不聚合(non Gathering)。聚合表示在同一个内存属性的区域中允许把多次访问内存的操作合并成一次总线传输。 若一个内存地址标记为“nG”,则会严格按照访问内存的次数和大小来访问内存,不会做合并优化。 若一个内存地址标记为“G”,则会做总线合并访问,如合并两个相邻的字节访问为一次多字节访问。若程序访问同一个内存地址两次,那么处理器只会访问内存一次,但是在第二次访问内存指令后返回相同的值。若这个内存区域标记为“nG”,那么处理器则会访问内存两次。 R和nR:分别表示指令重排(Re-ordering)与不重排(non Re-ordering)。 E和nE:分别表示提前写应答(Early Write Acknowledgement)与不提前写应答(non Early Write Acknowledgement)。往外部设备写数据时,处理器先把数据写入写缓冲区(write buffer)中,若使能了提前写应答,则数据到达写缓冲区时会发送写应答;若没有使能提前写应答,则数据到达外设时才发送写应答。

Linux内核中定义了如下几个内存属性。

<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))

内存属性如此重要,那系统在什么时候配置MAIR和内存属性呢?系统在上电复位并经过BIOS或者BootLoader初始化后跳转到内核的汇编代码。而在汇编代码中会对内存属性进行初始化。