Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aarch64体系结构与编程9--cache一致性介绍 #55

Open
jason--liu opened this issue Feb 8, 2021 · 0 comments
Open

aarch64体系结构与编程9--cache一致性介绍 #55

jason--liu opened this issue Feb 8, 2021 · 0 comments
Labels

Comments

@jason--liu
Copy link
Owner

jason--liu commented Feb 8, 2021

为什么要cache一致性( cache coherency )?

  • 系统中各级cache都有不同的数据备份,例如每个CPU核心都有L1 cache
  • cache一致性关注的是同一个数据在多个高速缓存和内存中的一致性问题,解决高速缓存一致性的方法主要是总线监听协议,例如MESI协议等

需要关注cache一致性的例子:
a. 驱动中使用DMA(数据cache和内存不一致)
b. Self-modifying code(数据cache的数据可能比指令cache新)
c. 修改了页表(TLB里保存的数据可能过时)

  • 大多时候硬件来维护cache一致性,不过有些场景下需要软件来手动保证cache一致性

ARM的cache一致性的演进

image

  • 单核处理器(Cortex-A8)

    • 单核,没有cache一致性问题
    • Cache管理指令仅仅作用于单核
  • 多核处理器(Cortex-A9 MP以及之后的处理器)

    • 硬件上支持cache一致性
    • Cache管理指令会广播到其他CPU核心
  1. 在2006年的时候,cortex-a8处理器横空出世,不过cortex-a8是一个单核的设计,单核处理器只有一个CPU,没有多核之间的cache不一致的问题,不过会有DMA和cache的一致性问题。

  2. 到了cortex-a9的时候,就有了多核设计(MPcore)了。多核设计的就需要在核与核之间通过硬件来保证cache的一致性问题,通常的做法是实现MESI类似的协议。

  3. 到了A15的时候,就出现了大小核的架构,大小核架构,相当于有两个cluster,每个cluster里有多个核心。cluster里的多个核心,我们就需要用mesi协议来保证一致性,那cluster与cluster之间呢?那么这时候就需要一个AMBA coherency extensions这样的缓存一致性的控制器来解决这个问题了。这个就是系统级别的cache一致性问题。Arm公司在这方面做了不少工作,有现成的IP可以使用,比如CCI-400,CCI-500等

一个例子

image

image
如上图, 第一个是单核处理器的情况,它只有一个cpu核心,和单一的cache,没有多核cache一致性问题。第二个图,是一个双核的处理器,每个核心内部有自己的cache,那么就需要一个硬件单元来完成这两个cache的一致性问题,通常就是我们说的SCU的硬件单元了。第三个图,要复杂一些,它由两个cluster组成,每个cluster有两个核心。我们来看一个cluster,它有一个缓冲一致性的硬件单元来保证core与core直接的一致性。那么在最下面有一个缓存一致性总线,或者缓存一致性控制器来保证这两个cluster之间的cache一致性问题

系统级别的cache一致性

系统cache一致性需要cache一致性内部总线(cache coherent interconnect)

  • AMBA 4 协议有ACE (AXI Coherency Extensions)
  • AMBA 5协议有CHI

image
我们来看一下系统级别的cache一致性问题。因为现在的arm系统越来越复杂了,从多核发展到多族,例如大小核架构等。比如下面这个图,这是一个典型的大小核架构,小核由A53来担当,大核由A72来担当,两个A53核心构成了一个cluster,这个cluster里,每个a53的cpu都有各自独立的L1 cache,然后一起共享一个L2 cache,然后通过一个ACE的硬件单元连接到缓存一致性总线里,ACE其实就是AXI coherent extensions的简称,这个是AMBA 4协议中定义的。我们再来看大核这边,它同样也是2个核心,每个核心都有一个L1 cache,也是共享一个L2 cache,同样也是通过ACE接口连接到缓存一致性总线上。那么除了CPU之外,系统还有GPU,比如arm公司的mali GPU,还有一些带有DMA的外设等,这些设备他们都有独立访问内存的能力,比如GPU还自己带有cache,那么他们也必须通过ACE接口来连接到这个缓存一致性总线上。这个缓存一致性总线,就是用来实现系统级别的cache一致性的

Cache一致性的解决方案

  1. 关闭cache

    • 优点:简单
    • 缺点:性能低下,功耗增加
  2. 软件维护cache一致性

    • 优点:硬件RTL实现简单
    • 缺点:a. 软件复杂度增加。软件需要手动clean/flush cache或者invalidate cache. b. 增加调试难度 c. 降低性能,和增加功耗
  3. 硬件维护cache一致性
    MESI协议来维护多核cache一致性。ACE接口来实现系统级别的cache一致性

    • 优点:对软件透明
    • 缺点:增加了硬件RTL实现难度和复杂度

多核之间的cache一致性

  • 多核CPU产生cache一致性的原因:同一个内存数据在多个CPU核心的L1 cache中存在多个不同的副本,导致数据不一致
  • 维护cache一致性的关键是跟踪每一个cache line的状态,并根据处理器的读写操作和总线上的相应传输来更新cache line在不同CPU内核上的cache的状态,从而维护cache一致性。

Cache一致性协议:

  • 监听协议(snooping protocol),每个高速缓存都要被监听或者监听其他高速缓存的总线活动
  • 目录协议(directory protocol),全局统一管理高速缓存状态

MESI协议:

  • 1983年,James Goodman提出Write-Once总线监听协议,后来演变成目前最流行的MESI协议
  • 所有的总线传输事务对于系统内所有的其他单元是可见的,因为总线是一个基于广播通信的介质,因而可以由每个处理器的高速缓存来进行监听。

image

MESI协议

每个cache line有4个状态:

  • 修改(Modified)
  • 独占(Exclusive)
  • 共享(Shared)
  • 失效(Invalid )

M:这行数据有效,数据已被修改,和内存中的数据不一致,数据只存在于该高速缓存中
E:这行数据有效,数据和内存中数据一致,数据只存在于该高速缓存中
S:这行数据有效,数据和内存中数据一致,多个高速缓存有这行数据的副本
I:这行数据无效

上面有三个状态都表示这一行cache line数据有效,那怎么去区别呢?
修改和独占状态的高速缓存行中,数据都是独有的,不同点在于修改状态的数据是脏的,和内存不一致;独占状态的数据是干净的,和内存一致。拥有修改状态的高速缓存行会在某个合适的时刻把该高速缓存行写回内存中,其后的状态变成共享状态。

共享状态的高速缓存行中,数据和其他高速缓存共享,只有干净的数据才能被多个高速缓存共享。

失效状态表示这个高速缓存行无效

MESI操作

本地读写:本地CPU读写自己私有的cache line,这是一个私有的操作
总线读写:读写远端CPU的cache line,CPU可以发送请求到总线上,所有的CPU都可以收到这个请求
image

image
初始状态下,当cache line中没有加载任何数据时,状态为I

MESI状态转换

当初始化状态为I时,并且发出本地读操作
image

  1. 如果CPU1发现本地副本,并且这个高速缓存行的状态为S,那么在总线上回复一个FlushOpt信号,即把当前的cache line的内容发送到总线上,那么刚才发出PrRd请求的CPU0,就能得到这个cache line的数据,然后CPU0状态变成S。那么这个时候的cache line的变化情况是:CPU0上的cache line从I编程S,CPU1上的cache line保存S不变
  2. 假设CPU2发现本地副本并且高速缓存行的状态为E,则在总线上回应FlushOpt信号,即把当前的cache line的内容发送到总线上,CPU2上的高速缓存行的状态变成S。那么这个时候 cache line的变化情况:CPU0的cache line从I变成了S,而CPU2上的cache line从E变成了S
  3. 假设CPU3发现本地副本并且高速缓存行的状态为M,将数据更新到内存,这时候两个高速缓存行的状态都为S。我们来看一下cache line的变化情况:CPU0上cache line从I变成了S,CPU3上的cache line从M变成了S
  4. 假设CPU1,CPU2, CPU3上的cache line都没有缓存数据,状态都是I,那么CPU0会从内存中读取数据到L1 cache,把cache line状态设置为E

当初始化状态为I时,并且发出本地写操作
image
我们假设CPU0发起了本地写请求,即CPU0发出读PrWr请求:

  1. 由于本地cache line是无效的,所以,CPU0发送BusRdX信号到总线上。这种情况本地写操作,变成了总线写了,我们要看其他CPU的情况
  2. 其他CPU(例如CPU1等)收到BusRdX信号,先检查自己的高速缓存中是否有缓存副本,广播应答信号
  3. 假设CPU1上有这份数据的副本,且状态为S,CPU1收到一个BusRdX信号指挥,会回复一个flushopt信号,把数据发送到总线上,然后把自己的cache line设置为无效,状态变成I,然后广播应答信号
  4. 假设CPU2上有这份数据的副本,且状态为E,CPU2收到这个BusRdx信号之后,会回复一个flushopt信号,把数据发送到总线上,会把自己的cache line设置为无效,然后广播应答信号
  5. 假设CPU3上有这份数据的副本,状态为M,CPU3收到这个BusRdx信号之后,会把自己cache line的内存flush为内存,然后自己状态变成I,然后广播应答信号
  6. 若其他CPU上也没有这份数据的副本,也要广播一个应答信号
  7. CPU0会接收其他CPU的所有的应答信号,确认其他CPU上没有这个数据的缓存副本后。CPU0会从总线上或者从内存中读取这个数据:
  • 如果其他CPU的状态是S或者E的时候,会把最新的数据通过flushopt信号发送到总线上
  • 如果总线上没有数据,那么直接从内存中读取数据
  • 最后才修改数据,并且本地cache line的状态变成M

当初始化状态为M时
image
当本地CPU的cache line状态为M的情况,最简单就是本地读写了,因为M的状态,说明只有你有最新的数据,而且是dirty的数据,所以本地读写,状态不变

收到一个总线读的信号

本地CPU(假设是CPU0)上的cache line的状态为M,而在其他CPU上没有这个数据的副本。当其他CPU(如CPU1)想读这份数据时,CPU1会发起一次总线读操作,所以,流程是这样的:

a. 若CPU0上有这个数据的副本,那么CPU0收到信号后把cache line的内容发送到总线上,然后CPU1就获取这个cache line的内容。另外,CPU0会把相关内容发送到主内存中,把cache line的内容写入主内存中。这时候CPU0的状态从M->S

b. 更改CPU1上的cache line状态为S

收到一个总线写的信号

数据在本地CPU(假设是CPU0)上有副本并且状态为M,而其他CPU上没有这个数据的副本。若某个CPU(假设CPU1)想更新(写)这份数据,CPU1就会发起一个总线写操作。

若CPU0上有这个数据的副本,CPU0收到总线写信号后,把自己的高速缓存行的内容发送到内存控制器,并把该cache line的内容写入主内存中。CPU0上的高速缓存行状态变成I

CPU1从总线或者内存中取回数据到本地cache line,然后修改自己本地cache line的内容。
CPU1的状态变成M

当初始化状态为S时
image

  1. 如果CPU发出本地读操作。状态不变
  2. 如果CPU收到总线读(BusRd),状态不变,回应一个flushopt信号,把数据发到总线上
  3. 如果CPU发出本地写操作(PrWr)
  • 发送BusRdX信号到总线上。
  • 本地CPU修改本地高速缓存行的内容,状态变成M。
  • 发送BusUpgr信号到总线上。
  • 其他CPU收到BusUpgr信号后,检查自己的高速缓存中是否有副本,若有,将其状态改成I

初始化状态为E的cache line
image

  1. 本地读,从该cache line中取数据,状态不变
  2. 本地写,修改该cache line的数据,状态变成M
  3. 收到一个总线读信号,独占状态的cache line是干净的,因此状态变成S。
  • cache line的状态变成S。
  • 发送FlushOpt信号,把cache line的内容发送到总线上。
  • 发出总线读信号的CPU,从总线上获取了数据,状态变成S。
  1. 收到一个总线写,数据被修改,该cache line不能再使用了,状态变成I。
  • cache line的状态变成I。
  • 发送Flushopt信号,把cache line的内容发送到总线上。
  • 发出总线写信号的CPU,从总线上获取了数据,然后修改,状态变成M。

MESI协议各个状态的转换关系(表格版)

image
image
image

MESI协议分析的一个例子

假设系统中有4个CPU,每个CPU都有各自的一级缓存,它们都想访问相同地址的数据A,大小为64字节。

  • T0时刻:4个CPU的L1 cache都没有缓存数据A,cache line的状态为I (无效的)
  • T1时刻:CPU0率先发起访问数据A的操作
  • T2时刻:CPU1也发起读数据操作
  • T3时刻:CPU2的程序想修改数据A中的数据
    请分析上述过程中, MESI状态的变化
  1. T0时刻,假设初始状态下数据A还没有缓存到cache中,4个CPU的cache line的默认状态是无效的
    image
  2. T1时刻,CPU0率先发起访问数据A的操作。对于CPU0来说,这是一次本地读。由于CPU0本地的高速缓存并没有缓存数据A,因此,CPU0首先发送一个BusRd信号到总线上。它想询问一下其他3个CPU:“小伙伴们,你们有缓存数据A吗?有的话,麻烦发一份给我。”其他3个CPU收到BusRd信号后,马上查询本地高速缓存,然后给CPU0回应一个应答信号。若CPU1在本地查询到缓存副本,则它把高速缓存行的内容发送到总线上并回应CPU0道:“CPU0,我这里缓存了一份副本,我发你一份。”若CPU1在本地没有缓存副本,则回应:“CPU0,我没有缓存数据A。”假设CPU1上有缓存副本,那么CPU1把缓存副本发送到总线上,CPU0的本地缓存就有了数据A,并且把这个高速缓存行的状态设置为S。同时,提供数据的缓存副本的CPU1也知道一个事实,数据的缓存副本已经分享给CPU0了,因此CPU1的高速缓存行的状态也设置为S。在本场景中,如果其他3个CPU都没有数据的缓存副本,那么CPU0只能老老实实地从主内存中读取数据A并将其缓存到CPU0的cache line中,把高速缓存行的状态设置为E
    image
  3. T2时刻,CPU1也发起读数据操作。这时,整个系统里只有CPU0中有缓存副本,CPU0会把缓存的数据发送到总线上并且应答CPU1,最后CPU0和CPU1都有缓存副本,状态都设置为S。
    image
  4. T3时刻,CPU2的程序想修改数据A中的数据。这时CPU2的本地cache line并没有缓存数据A,高速缓存行的状态为I,因此,这是一次本地写操作。首先CPU2会发送BusRdX信号到总线上,其他CPU收到BusRdX信号后,检查自己的高速缓存中是否有该数据。若CPU0和CPU1发现自己都缓存了数据A,那么会使这些cache line无效,然后发送应答信号。虽然CPU3没有缓存数据A,但是它回复了一条应答信号,表明自己没有缓存数据A。CPU2收集完所有的应答信号之后,把CPU2本地的高速缓存行状态改成M,M状态表明这个高速缓存行已经被自己修改了,而且已经使其他CPU上相应的cache line无效
    image

高速缓存伪共享(False Sharing)

  • 伪共享:如果多个处理器同时访问一个缓存行中不同的数据时,带来了性能上的问题
  • 举个栗子:假设CPU0上的线程0想访问和更新struct data数据结构中的x成员,同理CPU1上的线程1想访问和更新struct data数据结构中的y成员,其中x和y成员都被缓存到同一个缓存行里

假设CPU0上的线程0想访问和更新data数据结构中的x成员,同理CPU1上的线程1想访问和更新data数据结构中的y成员,其中x和y成员都缓存到同一个高速缓存行里。
根据我们前面学会的MESI协议的状态图,我们可以分析出CPU0和CPU1之间对高速缓存行的争用情况

  1. CPU0第一次访问x成员时,因为x成员还没有缓存到高速缓存,所以cache line的状态为I。CPU0把整个data数据结构都缓存到CPU0的L1 cache里,并且把cache line的状态设置为E
    image
  2. CPU1第一次访问y成员时,因为y成员已经缓存到高速缓存中,而且该cache line的状态是E,所以CPU1先发送一个总线读的请求。CPU0收到请求后,先查询本地高速缓存中是否有这个数据的副本,若有,则把这个数据发送到总线上。CPU1获取了数据后,把本地的高速缓存行的状态设置为S,并且把CPU0上的本地cache line的状态也设置为S,因此所有CPU上对应的高速缓存行状态都设置为S
    image
  3. CPU0想更新x成员的值时,CPU0和CPU1上的高速缓存行的状态为S。CPU0发送BusUpgr信号到总线上,然后修改本地cache line的数据,将其状态变成M;其他CPU收到BusUpgr信号后,检查自己的高速缓存行中是否有副本,若有,则将其状态改成I
    image
  4. CPU1想更新y成员的值时,CPU1上的高速缓存行的状态为I,而CPU0上的cache line缓存了旧数据,并且状态为M。这时,CPU1发起本地写的请求,根据MESI协议,CPU1会发送BusRdX信号到总线上。其他CPU收到BusRdX信号后,先检查自己的高速缓存中是否有该数据的副本,广播应答信号。这时CPU0上有该数据的缓存副本,并且状态为M。CPU0先将数据更新到内存,更改其高速缓存行状态为I,然后发送应答信号到总线上。CPU1收到所有CPU的应答信号后,才能修改CPU1上高速缓存行的内容。最后,CPU1上cache line的状态变成M
    image
  5. 若CPU0想更新x成员的值,这和步骤(4)类似,发送本地写请求后,根据MESI协议,CPU0会发送BusRdX信号到总线上。CPU1接收该信号后,把高速缓存行数据写回内存,然后使该高速缓存行无效,即把CPU1上的高速缓存行状态变成I,然后广播应答信号。CPU0收到所有CPU的应答信号后才能修改CPU0上的高速缓存行内容。最后,CPU0上的高速缓存行的状态变成M
    image

综上所述,如果CPU0和CPU1反复修改,就会不断地重复步骤(4)和步骤(5),两个CPU都在不断地争夺对高速缓存行的控制权,不断地使对方的高速缓存行无效,不断地把数据写回内存,导致系统性能下降,这种现象叫作高速缓存伪共享

高速缓存伪共享的解决办法

高速缓存伪共享的解决办法就是让多线程操作的数据处在不同的高速缓存行,通常可以采用高速缓存行填充(padding)技术或者高速缓存行对齐(align)技术,即让数据结构按照高速缓存行对齐,并且尽可能填充满一个高速缓存行大小
下面的代码定义一个counter_s数据结构,它的起始地址按照高速缓存行的大小对齐,数据结构的成员通过pad[4]来填充。这样, counter_s的大小正好 是一个cache line的大小,64个字节,而且它的起始地址也是也cache line对齐
image

系统间的cache一致性

系统cache一致性的解决方案,其实在arm公司实现的AMBA总线里,AMBA总线协议里定义了一个叫做ACE的接口,这个接口规范执行了Hardware Cache Coherency
cache一致性,需要保证系统中所有的CPU,所有的bus master从,例如GPU,DMA等,他们观察到的内存是一致的
一般情况下系统的cache一致性有三种方案:我们在前面已经和大家分析过了。

  1. 关闭cache。这是最简单的办法,不过,它会严重影响性能。
  2. 软件管理cache一致性。这是最常用的方式,软件需要在合适的时候去clean or flush dirty cache,或者invalidate old data。这种方式需要编写驱动的工程师,特别小心。
  3. 硬件管理cache一致性。对软件是透明的。

基于ACE接口协议,arm公司设计了几款cache一致性的控制器。

  1. 提供AMBA 4 Coherency Extensions(ACE)
  2. 支持1~6个ACE master
  3. ACE提供了基于MOESI-base的协议来保证cross-cluster一致性

CCI缓存一致性控制器

这里列出了ARM公司几款CCI控制器,CCI就是Cache Coherent Interconnect的简称
image
我们以最常见的CCI-400为例,它支持2个cluster,最大支持8个CPU,支持2个ACE slave接口,支持1~3个ACE lite slave接口,然后他使用broadcast snoop coherency策略
下面这个框图是经典的使用大小核的框图,并且使用了CCI-400这个缓存一致性的控制器
image
大家可以看到,中间这个就是CCI-400的控制器,那么大小核的A57和A53都连接到这个CCI-400上,还有Mali GPU也连接到CCI-400里,除此之外,还有DDR内存,通过DMC-400的内存控制器连接到CC400里,当然还有一些外设,也可以通过ACE Lite接口连接到 CCI400上

CCN缓存一致性控制器

ARM一直想冲击服务器市场,服务器一般cpu核心的数量都是几十个,上百个的,所以,刚才介绍的CCI控制器显然不能满足服务器的需求,所以,arm重新设计了一个新的缓存一致性控制器,叫做Cache Coherent Network,简称CCN,它可以支持48个核心,L3 cache,而且The L3 system cache allows IO and accelerators to allocate memory on chip。这个CCN是基于最新的AMBA 5协议的。

这个表格就是几款CCN IP的规格,最高规格的CCN-512,最大支持48个核心,支持12个cluster,支持L2 cache,1-32MB,最高达到225GB每秒的传输数率
image
CCN的这个IP的芯片手册现在还没有公开下载,所以,我们只能通过一些简单的介绍资料来了解。
下面这个图是一个典型的CCN控制器的应用场景。这个一个服务器的配置,它支持48个A72,集成了32MB的L3 cache,还支持DDR3/4内存颗粒,支持24 个cache一致性的IO port。总之就是一个典型的服务器配置
image

高速缓存伪共享的避免

  1. 一些常用的数据结构在定义时就约定数据结构以一级缓存对齐。例如使用如下的宏来让数据结构首地址以L1cache对齐
#define cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES)))
  1. 数据结构中频繁访问的成员可以单独占用一个高速缓存行,或者相关的成员在高速缓存行中彼此错开,以提高访问效率 。
    例如 struct zone 数据结构使用 ZONE_PADDING 技术(填充字节的方式)来让频繁访问的成员在不同的 cache line 中
struct zone {
...
spinlock_t
lock;
ZONE_PADDING(_pad2_)
spinlock_t
lru_lock;
...
} ____cacheline_in_smp;

DMA的cache一致性

  • DMA(Direct Memory Access)直接内存访问,它在传输过程中是不需要CPU干预的,可以直接从内存中读写数据
  • DMA产生cache一致性问题的原因:
    • DMA 直接操作系统总线来读写内存地址,而 CPU 并不感知。
    • 如果 DMA 修改的内存地址,在 CPU 的 cache 中有缓存,那么 CPU 并不知道内存数据被修改了, CPU 依然去访问 cache 的旧数据,导致Cache 一致性问题

image

DMA的cache一致性解决方案

  • 硬件解决办法,需要ACE支持(咨询SoC vendor)

  • 使用non-cacheable的内存来进行DMA传输

    • 缺点:在不使用 DMA 的时候, CPU 访问这个 buffer 会导致性能下降
  • 软件干预cache一致性,根据DMA传输数据的方向,软件来维护cache一致性

Case 1:内存->设备FIFO (设备例如网卡,通过DMA读取内存数据到设备FIFO)

在DMA传输之前,CPU的cache可能缓存了内存数据,需要调用cache clean/flush操作,把cache内容写入到内存中。因为CPU cache里可能缓存了最新的数据
image
Case 2:设备FIFO -> 内存 (设备把数据 写入到 内存中)
在这个场景下,设备的FIFO产生了新数据,需要把数据写入到DMA buffer里,然后主机的软件就可以读到设备的数据,这个非常类似网卡的收包的过程.
在DMA传输之前,需要把cache 做invalid操作。因为此时最新数据在设备FIFO中,CPU缓存的cache数据是过时的,一会要写入新数据,所以做invalid操作
image
DMA小结:

  1. 在启动DMA之前,最新的数据源在哪里?是在CPU那侧还是设备那侧?
  2. 在启动DMA之前,cache保存的数据 是最新的还是 过时的?
    把这两个问题想清楚了,就能知道是要clean cache还是要invalidate cache里

self-modifying code

  • 指令cache和数据cache是分开的。指令cache一般只读
  • 指令cache和数据cache的一致性问题。指令通常不能修改,但是某些特殊情况指令存在被修改的情况
  • self-modifying code,在执行过程中修改自己的指令,过程如下:
    • 把要修改的指令,加载到数据cache里
    • 程序(CPU)修改新指令,数据cache里缓存了最新的指令

问题:

  • 指令cache依然缓存了 旧的指令。
  • 新指令还在数据cache里

self-modifying code解决办法:
解决的思路是,使用cache管理指令和内存屏障指令来保证数据cache和指令cache的一致性。
在armv8手册里,有一段简单的英文来介绍self-modifying code,它是在Armv8.6手册最后面术语一章里面。在这段简单的英文里,我们可以得到重要的信息,就是解决self-modifying code的指令cache和数据cache不一致的问题,需要使用cache管理指令和内存屏障指令来手动干预
image
解决思路:

  1. 使用cache clean操作,把cache line的数据写回到内存。
  2. 使用DSB指令保证其他观察者看到clean操作已经完成
  3. 无效指令cache
  4. 使用DSB指令确保其他观察者看到无效操作已经完成
  5. ISB指令让程序重新预取指令

在armv8.6手册里,有一段描述这个过程的, 在B2.4.4
image

参考资料

https://blog.csdn.net/armlinuxww/article/details/110228343?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242

https://zhuanlan.zhihu.com/p/135462276

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant