转自CPU CACHE中的VIPT与PIPT的工作原理_Linux与SoC的博客-CSDN博客

启动信息描述
内核启动过程中有如下打印信息:

CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
1
这行打印信息代表了处理器L1 CACHE所支持的寻址方式。

在kernel启动过程中,虽然这里第一次出现CACHE相关的打印信息,但是,此处并不是kernel第一次操作CACHE。
例如:对于zImage而言,解压缩过程中会开启CACHE并配置CACHE 写属性。

#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
#define CB_BITS 0x08
#else
#define CB_BITS 0x0c
#endif

CPU初始化的汇编文件head.S中,会根据kernel配置来使能CACHE。

#ifdef CONFIG_CPU_DCACHE_DISABLE
        bic     r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
        bic     r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
        bic     r0, r0, #CR_I
#endif

关于这条打印信息,在10几年前有过一次精彩的讨论。具体查看下面的PATCH。
[PATCH] arm: ARMv7 cache info printk

代码分析
以上打印信息源自于 kernel 代码的setup.c中。

static void __init cacheid_init(void)
{
    unsigned int arch = cpu_architecture();

if (arch >= CPU_ARCH_ARMv6) {
        unsigned int cachetype = read_cpuid_cachetype();

if ((arch == CPU_ARCH_ARMv7M) && !(cachetype & 0xf000f)) {
            cacheid = 0;
        } else if ((cachetype & (7 << 29)) == 4 << 29) {
            /* ARMv7 register format */
            arch = CPU_ARCH_ARMv7;
            cacheid = CACHEID_VIPT_NONALIASING;
            switch (cachetype & (3 << 14)) {
            case (1 << 14):
                cacheid |= CACHEID_ASID_TAGGED;
                break;
            case (3 << 14):
                cacheid |= CACHEID_PIPT;
                break;
            }
        } else {
            arch = CPU_ARCH_ARMv6;
            if (cachetype & (1 << 23))
                cacheid = CACHEID_VIPT_ALIASING;
            else
                cacheid = CACHEID_VIPT_NONALIASING;
        }
        if (cpu_has_aliasing_icache(arch))
            cacheid |= CACHEID_VIPT_I_ALIASING;
    } else {
        cacheid = CACHEID_VIVT;
    }

pr_info("CPU: %s data cache, %s instruction cache\n",
        cache_is_vivt() ? "VIVT" :
        cache_is_vipt_aliasing() ? "VIPT aliasing" :
        cache_is_vipt_nonaliasing() ? "PIPT / VIPT nonaliasing" : "unknown",
        cache_is_vivt() ? "VIVT" :
        icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" :
        icache_is_vipt_aliasing() ? "VIPT aliasing" :
        icache_is_pipt() ? "PIPT" :
        cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown");
}

通过cpu_architecture()获取处理器内核版本号

int __pure cpu_architecture(void)
{
    BUG_ON(__cpu_architecture == CPU_ARCH_UNKNOWN);

return __cpu_architecture;
}

处理器内核版本号通过数据结构proc_arch来维护,数据结构成员的值代表的ARM-vXXX

static const char *proc_arch[] = {
    "undefined/unknown",
    "3",
    "4",
    "4T",
    "5",
    "5T",
    "5TE",
    "5TEJ",
    "6TEJ",
    "7",
    "7M",
    "?(12)",
    "?(13)",
    "?(14)",
    "?(15)",
    "?(16)",
    "?(17)",
};

处理器内核版本编号如下,例如__cpu_architecture为4则代表的是ARM-v5内核。

#define CPU_ARCH_UNKNOWN    0
#define CPU_ARCH_ARMv3        1
#define CPU_ARCH_ARMv4        2
#define CPU_ARCH_ARMv4T        3
#define CPU_ARCH_ARMv5        4
#define CPU_ARCH_ARMv5T        5
#define CPU_ARCH_ARMv5TE    6
#define CPU_ARCH_ARMv5TEJ    7
#define CPU_ARCH_ARMv6        8
#define CPU_ARCH_ARMv7        9
#define CPU_ARCH_ARMv7M        10

需要注意的这个范围内获取处理器内核版本号,不是通过读取MIDR寄存器来实现的。

针对具体的ARM 内核版本,获取CACHE所支持的寻址方式,并据此初始化cacheid。后面以cacheid为依据对CACHE的所支持的寻址方式进行分类。

在ARM处理器内部包含了CACHE类型信息的只读寄存器,这在芯片设计初期便已经定义好,后期无法修改。在这个寄存器中包含了CACHE查找策略。对于ARM v系列的处理器内核而言,获取CACHE类型的指令代码如下:

#define read_cpuid(reg)                            \
    ({                                \
        unsigned int __val;                    \
        asm("mrc    p15, 0, %0, c0, c0, " __stringify(reg)    \
            : "=r" (__val)                    \
            :                            \
            : "cc");                        \
        __val;                            \
    })

基于读取CACHE类型寄存器得到信息初始化cacheid,利用cachetype.h中定义的宏,打印出cache类型。从kenrel启动的日志信息中可以看出,当前CPU内核的CACHE类型如下:

DCACHE
PIPT / VIPT nonaliasing
ICACHE
VIPT nonaliasing
对于ARM处理器而言,不同内核版本的CACHE类型如下:


基于cacheid定义了如下的宏定义:

#define cache_is_vivt()            cacheid_is(CACHEID_VIVT)
#define cache_is_vipt()            cacheid_is(CACHEID_VIPT)
#define cache_is_vipt_nonaliasing()    cacheid_is(CACHEID_VIPT_NONALIASING)
#define cache_is_vipt_aliasing()    cacheid_is(CACHEID_VIPT_ALIASING)
#define icache_is_vivt_asid_tagged()    cacheid_is(CACHEID_ASID_TAGGED)
#define icache_is_vipt_aliasing()    cacheid_is(CACHEID_VIPT_I_ALIASING)
#define icache_is_pipt()        cacheid_is(CACHEID_PIPT)
...
static inline unsigned int __attribute__((pure)) cacheid_is(unsigned int mask)
{
    return (__CACHEID_ALWAYS & mask) |
           (~__CACHEID_NEVER & __CACHEID_ARCH_MIN & mask & cacheid);
}

而本文开头中打印出来的信息,就是基于上面这些宏得出的结果。

通常来说,CACHE得寻址类型包括VIPT、PIPT、VIVT,而本文重点分析得是VIPT以及PIPT的工作原理。

VIPT和PIPT
具备MMU(TLB)、CACHE的ARM处理器,其地址翻译的流程如下:

那么上图中红框内的CACHE部分,根据索引或标签对应的是物理地址还是虚拟地址,将CACHE的寻址方式分为VIPT CACHE和PIPT CACHE。

VI的含义是使用虚拟地址来构建缓存索引(index)。反之,PI的含义是使用物理地址来构建缓存索引。

缓存索引用于从缓存中提取标记,将它与从物理地址计算的缓存标记比对。如果匹配,则缓存中命中该查找,从缓存中提取相关的数据。否则,将从下一层缓存(或从内存)中提取。

PT的含义是使用物理地址来构建缓存标记(tag),之所以使用PT作为CACHE查找索引是为了解决VIVT中存在的索引冲突,即两个进程可以为不同的物理地址使用相同的虚拟地址。

VIPT
VIPT CACHE使用物理地址作为Tag,逻辑地址作为Index。通过Index查询CACHE获取到物理地址中的tag部分。同时呢,利用逻辑地址去查TLB,在TLB中获取到物理地址。然后将CACHE中查询到的物理地址Tag部分,同TLB中获取到的物理地址Tag部分作比较。若二者相同,则CACHE hit,否则CACHE miss。

aliasing
对于VIPT CACHE而言,virtual_addr_A和virtual_addr_B虽然不同,但是它们指向了同一个物理地址PA。例如下面的代码:

mmap(virtual_addr_A,4096,prot,flags,file_descriptor,offset)
mmap(virtual_addr_B,4096,prot,flags,file_descriptor,offset)
1
2
VIPT使用虚拟地址作为CACHE Index,因此物理地址A的数据在CACHE中有两份,分别由virtual_addr_A和virtual_addr_B进行管理。这样的情形称之为CACHE 别名。


可以通过下面的4种方法来避免别名问题带来的影响:

当进行内存数据更新时进行cache invalid操作

多副本数据同步更新

以上两种方法需要进程通过虚实地址转换等操作,获取到是否有副本数据存在这样的信息。而VIPT的设计初衷是避免虚实地址转换,因此,这两种方法并不是非常可取。

缩小CACHE size

假设对于一个32bit位宽的虚拟地址而言,他的page offset为12bit。若CACHE size小于4K则不会产生别名问题,假设CACHE size大于4K,则会产生别名问题。但是CACHE过小,cache miss又会大幅增加。

page color或者cache cloring

若以上3种方法都没有解决CACHE别名问题,那么可以使用缓存着手的方法,这完全是一种软件层面的解决方案。

PIPT
PIPT中的tag和index均为物理地址。而CPU发出的逻辑地址也称之为虚拟地址,因此,首先需要通过TLB或查询内存中的页表,将逻辑地址转换为对应的物理地址。再进行CACHE缓存查找。索引和标签都使用物理地址。虽然这很简单,避免了重名问题,但速度也很慢,因为必须先查找物理地址(这可能涉及TLB丢失和访问主内存),才能在缓存中查找该地址。

CPU CACHE中的VIPT与PIPT的工作原理相关推荐

  1. 理解vue中less的scoped和/deep/工作原理

    理解vue中less的scoped和/deep/工作原理 scoped /deep/ 实战 总结 scoped vue项目一般是单页面.多组件,整个项目共用一个css样式表,有时候我们在写组件的过程中 ...

  2. python中的函数、生成器的工作原理

    1.python中函数的工作原理 def foo():bar()def bar():pass python的解释器,也就是python.exe(c编写)会用PyEval_EvalFramEx(c函数) ...

  3. python 生成器 原理_你知道python中的函数、生成器的工作原理吗?

    1.python中函数的工作原理 python的解释器,也就是python.exe(c编写)会用PyEval_EvalFramEx(c函数)运行foo()函数 首先会创建一个栈帧(stack Fram ...

  4. spring web.xml中 过滤器(Filter)的工作原理和代码演示

    一.Filter简介 Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, ...

  5. oauth2.0授权协议中刷新令牌refresh token的工作原理及生命周期分析

    在学习oauth2.0协议的时候,对于刷新令牌refresh token感觉很困惑.主要是为啥需要刷新令牌,以及刷新令牌是如何工作的,技术细节是啥?比如通过refresh token可以让access ...

  6. 神经网络在控制中的作用,间歇控制器的工作原理

    什么是神经网络控制 神经网络控制技术是一项复杂的系统控制技术,一般应用在变频器的控制中,它是通过对系统的辨识.运算后对变频器进行控制的一种新技术. 而且神经网络控制可以同时控制多个变频器,所以应用在多 ...

  7. php mail函数详解,php中mail()函数用法和SMTP工作原理详解

    一个发送E-MAIL的脚本也许是你能够在Web 站点上找到的最普通的脚本之一了,虽然它很简单,一个邮件脚本有时会令程序员非常沮丧.PHP中有一个叫做mail()的函数,它只需要知道接收方的地址 和信件 ...

  8. Apache/Nginx Cache Last-Modified、Expires和Etag相关工作原理

    一篇不错的文章.http://xok.la/2010/11/apache_nginx_cache_last-modified.expires_etag.html   转载于:https://blog. ...

  9. 122网络各层中的设备是什么以及工作原理

    应用层:服务器.计算机 传输层:网关 网络层:路由器,防火墙.三层交换机(ip) 数据链路层:网桥.以太网交换机.网卡 物理层:中继器和集线器.网卡.双绞线.同轴电缆.光纤电缆 网桥和交换机区别: 1 ...

最新文章

  1. 配置hadoop集群一
  2. Facebook最新研究:配合AR眼镜使用的腕带,可将神经信号转化为动作
  3. SAP系统与外部系统的三种接口形式
  4. python列表常用方法_第24p,必须掌握,列表的常用方法
  5. MySQL高级 - 锁 - InnoDB行锁 - 行锁升级为表锁
  6. dotNET Core 3.X 依赖注入
  7. C++设计模式之Adapter
  8. Redis高可用架构 (redis主从+sentinel)
  9. javascript this的意思
  10. leetcode之逆波兰表达式
  11. Atitit 技术体系路线图与金字塔模型 前端技术层次金字塔 目录 1. 原理core 框架层 》》 模块 项目层 1 1.1. 技术原理层 1 1.2. 框架层 1 1.3. 模块 1 2.
  12. 2021年社工必备查询网址汇总
  13. win10 电脑开机底部任务栏无反应(鼠标一直转圈,部分图标不显示)
  14. 离线安装linux的gcc,nvidia等错误
  15. yii2 使用gii生成mongo模型 控制器以及YII2 多MongoDB配置和使用
  16. 为什么你宁愿吃生活的苦,也不愿吃学习的苦
  17. 数据与广告系列三十二:重排ReRank,广告推荐算法链路上的背叛者,生态系统格局的重塑者...
  18. 汉诺塔(河内塔)问题解析(函数递归经典问题)
  19. 计算机的综合应用图片,计算机软件综合应用.PPT
  20. 四旋翼飞行器9——APM地面站使用详解(校准结束)

热门文章

  1. 【探索-中级算法】中序遍历二叉树
  2. 每一个设计师需知的40个设计素材站
  3. Catalan 数的应用
  4. 版本号命名规则 软件版本说明
  5. 简单脚本测试--对谈式脚本@read
  6. Fama-French 三因子模型
  7. 使用python爬虫爬取最好大学网大学排名实例
  8. 串口转无线WiFi模块——WizFi210-EVB操作手册
  9. 计算机中分页符号是什么,电脑word文档中分页符如何插入
  10. 修改ptrace_scope