微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 硬件工程师文库 > 从硬件引申出内存屏障,带你深入了解Linux内核RCU

从硬件引申出内存屏障,带你深入了解Linux内核RCU

时间:08-19 来源:电子发烧友网工程师 点击:

3.CPU 1接收到CPU 0的"使无效"消息,将它排队,并立即响应该消息。

4.CPU 0接收到来自于CPU 1的响应消息,因此它放心的通过第4行的smp_mb(),从存储缓冲区移动"a"的值到缓存行。

5.CPU 0执行b = 1。它已经拥有这个缓存行(也就是说,缓存行已经处于"modified"或者"exclusive"状态),因此它将"b"的新值存储到缓存行中。

6.CPU 0接收到"读"消息,并且发送包含"b"的新值的缓存行到CPU 1,同时在自己的缓存中,标记缓存行为"shared"状态。

7.CPU 1接收到包含"b"的缓存行并且将其应用到本地缓存。

8.CPU 1现在可以完成while (b ==0) continue,因为它发现"b"的值为1,接着处理下一条语句。

9.CPU 1执行assert(a == 1),并且,由于旧的"a"值还在CPU 1的缓存中,因此陷入错误。

10.虽然陷入错误,CPU 1处理已经排队的"使无效"消息,并且(迟到)在自己的缓存中刷新包含"a"值的缓存行。

四、内存屏障

既然硬件设计者通过Write buffer和使无效队列引入了额外的内存乱序问题,那么就应当为软件工程师提供某种方法来解决这个问题。即使相应的解决方法会折磨软件工程师。

答案就是内存屏障。对于Linux内核资深工程师来说,这个答案也显得比较沉重,它太折磨人了:)

我们先看看Write buffer一节中,触发断言的例子,应该怎么修改。

在那个例子中,硬件设计者不能直接帮助我们,因为 CPU没有办法识别那些相关联的变量(例子中的a和b),更不用说它们如何关联。因此,硬件设计者提供内存屏障指令,以允许软件告诉CPU这些关系的存在。程序必须修改,以包含内存屏障:

 1 void foo(void)

  2 {

  3   a = 1;

  4   smp_mb();

  5   b = 1;

  6 }

  7

  8 void bar(void)

  9 {

 10   while (b == 0) continue;

 11   assert(a == 1);

 12 }

内存屏障smp_mb()将导致CPU在刷新后续的缓存行(包含b的缓存行)之前,前面的Write buffer被先刷新。在继续处理之前,CPU可能采取的动作是:

1、简单的停顿下来,直到存储缓冲区变成空;

2、也可能是使用存储缓冲区来持有后续的存储操作,直到前面所有的存储缓冲区已经被保存到缓存行中。

理解其中第2点,能够帮助我们理解"内存屏障"这个单词的来历!!

后一种情况下,操作序列可能如下所示:

1.CPU 0执行a= 1。缓存行不在CPU0的缓存中,因此CPU 0将"a"的新值放到存储缓冲中,并发送一个"读使无效"消息。

2.CPU 1 执行while(b == 0) continue,但是包含"b"的缓存行不在它的缓存中,因此它发送一个"读"消息。

3.CPU 0执行smp_mb(),并标记当前所有存储缓冲区的条目。(也就是说a = 1这个条目)。

4.CPU 0执行b= 1。它已经拥有这个缓存行了。(也就是说, 缓存行已经处于"modified"或者"exclusive"状态),但是在存储缓冲区中存在一个标记条目。因此,它不将"b"的新值存放到缓存行,而是存放到存储缓冲区中。(但是"b"不是一个标记条目)。

5.CPU 0接收"读"消息,随后发送包含原始"b"值的缓存行给CPU1。它也标记该缓存行的复制为"shared"状态。

6.CPU 1读取到包含"b"的缓存行,并将它复制到本地缓存中。

7.CPU 1现在可以装载"b"的值了,但是,由于它发现其值仍然为"0",因此它重复执行while语句。"b"的新值被安全的隐藏在CPU0的存储缓冲区中。

8.CPU 1接收到"读使无效"消息,发送包含"a"的缓存行给CPU 0,并且使它的缓存行无效。

9.CPU 0接收到包含"a"的缓存行,使用存储缓冲区的值替换缓存行,将这一行设置为"modified"状态。

10.由于被存储的"a"是存储缓冲区中唯一被smp_mb()标记的条目,因此CPU0能够存储"b"的新值到缓存行中,除非包含"b"的缓存行当前处于"shared"状态。

11.CPU 0发送一个"使无效"消息给CPU 1。

12.CPU 1接收到"使无效"消息,使包含"b"的缓存行无效,并且发送一个"使无效应答"消息给 CPU 0。

13.CPU 1执行while(b == 0) continue,但是包含"b"的缓存行不在它的缓存中,因此它发送一个"读"消息给 CPU 0。

14.CPU 0接收到"使无效应答"消息,将包含"b"的缓存行设置成"exclusive"状态。CPU 0现在存储新的"b"值到缓存行。

15.CPU 0接收到"读"消息,同时发送包含新的"b"值的缓存行给 CPU 1。它也标记该缓存行的复制为"shared"状态。

16.CPU 1接收到包含"b"的缓存行,并将它复制到本地缓存中。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top