<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>系统编程 on codedump notes</title>
    <link>https://www.codedump.info/zh/tags/%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B/</link>
    <description>Recent content in 系统编程 on codedump notes</description>
    <generator>Hugo</generator>
    <language>zh</language>
    <lastBuildDate>Sat, 14 Dec 2019 22:41:22 +0800</lastBuildDate>
    <atom:link href="https://www.codedump.info/zh/tags/%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>C&#43;&#43;11中的内存模型下篇 - C&#43;&#43;11支持的几种内存模型</title>
      <link>https://www.codedump.info/zh/post/20191214-cxx11-memory-model-2/</link>
      <pubDate>Sat, 14 Dec 2019 22:41:22 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20191214-cxx11-memory-model-2/</guid>
      <description>&lt;p&gt;在本系列的上篇，介绍了内存模型的基本概念，接下来看C++11中支持的几种内存模型。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;几种关系术语&#34;&gt;&#xA;  几种关系术语&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e5%87%a0%e7%a7%8d%e5%85%b3%e7%b3%bb%e6%9c%af%e8%af%ad&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;在接着继续解释之前，先了解一下几种关系术语。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;sequenced-before&#34;&gt;&#xA;  sequenced-before&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#sequenced-before&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;sequenced-before用于表示&lt;strong&gt;单线程&lt;/strong&gt;之间，两个操作上的先后顺序，这个顺序是非对称、可以进行传递的关系。&lt;/p&gt;&#xA;&lt;p&gt;它不仅仅表示两个操作之间的先后顺序，还表示了操作结果之间的可见性关系。两个操作A和操作B，如果有A sequenced-before B，除了表示操作A的顺序在B之前，还表示了操作A的结果操作B可见。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;happens-before&#34;&gt;&#xA;  happens-before&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#happens-before&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;与sequenced-before不同的是，happens-before关系表示的&lt;strong&gt;不同线程&lt;/strong&gt;之间的操作先后顺序，同样的也是非对称、可传递的关系。&lt;/p&gt;&#xA;&lt;p&gt;如果A happens-before B，则A的内存状态将在B操作执行之前就可见。在上一篇文章中，某些情况下一个写操作只是简单的写入内存就返回了，其他核心上的操作不一定能马上见到操作的结果，这样的关系是不满足happens-before的。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;synchronizes-with&#34;&gt;&#xA;  synchronizes-with&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#synchronizes-with&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;synchronizes-with关系强调的是变量被修改之后的传播关系（propagate），即如果一个线程修改某变量的之后的结果能被其它线程可见，那么就是满足synchronizes-with关系的。&lt;/p&gt;&#xA;&lt;p&gt;显然，满足synchronizes-with关系的操作一定满足happens-before关系了。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;c11中支持的内存模型&#34;&gt;&#xA;  C++11中支持的内存模型&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#c11%e4%b8%ad%e6%94%af%e6%8c%81%e7%9a%84%e5%86%85%e5%ad%98%e6%a8%a1%e5%9e%8b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;从C++11开始，就支持以下几种内存模型：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;enum memory_order {&#xA;    memory_order_relaxed,&#xA;    memory_order_consume,&#xA;    memory_order_acquire,&#xA;    memory_order_release,&#xA;    memory_order_acq_rel,&#xA;    memory_order_seq_cst&#xA;};&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;与内存模型相关的枚举类型有以上六种，但是其实分为四类，如下图所示，其中对一致性的要求逐渐减弱，以下来分别讲解。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;c&amp;#43;&amp;#43;model&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-2/c&amp;#43;&amp;#43;model.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; c++model &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;memory_order_seq_cst&#34;&gt;&#xA;  memory_order_seq_cst&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#memory_order_seq_cst&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;这是默认的内存模型，即上篇文章中分析过的顺序一致性内存模型，由于在上篇中的相关概念已经做过详细的介绍，这里就不再阐述了。仅列出引用自《C++  Concurrency In Action》的示例代码。&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;atomic&amp;gt;&#xA;#include &amp;lt;thread&amp;gt;&#xA;#include &amp;lt;assert.h&amp;gt;&#xA;&#xA;std::atomic&amp;lt;bool&amp;gt; x,y;&#xA;std::atomic&amp;lt;int&amp;gt; z;&#xA;&#xA;void write_x()&#xA;{&#xA;    x.store(true,std::memory_order_seq_cst);&#xA;}&#xA;&#xA;void write_y()&#xA;{&#xA;    y.store(true,std::memory_order_seq_cst);&#xA;}&#xA;&#xA;void read_x_then_y()&#xA;{&#xA;    while(!x.load(std::memory_order_seq_cst));&#xA;    if(y.load(std::memory_order_seq_cst))&#xA;        ++z;&#xA;}&#xA;&#xA;void read_y_then_x()&#xA;{&#xA;    while(!y.load(std::memory_order_seq_cst));&#xA;    if(x.load(std::memory_order_seq_cst))&#xA;        ++z;&#xA;}&#xA;&#xA;int main()&#xA;{&#xA;    x=false;&#xA;    y=false;&#xA;    z=0;&#xA;    std::thread a(write_x);&#xA;    std::thread b(write_y);&#xA;    std::thread c(read_x_then_y);&#xA;    std::thread d(read_y_then_x);&#xA;    a.join();&#xA;    b.join();&#xA;    c.join();&#xA;    d.join();&#xA;    assert(z.load()!=0);&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;由于采用了顺序一致性模型，因此最后的断言不可能发生，即在程序结束时不可能出现z为0的情况。&lt;/p&gt;</description>
    </item>
    <item>
      <title>C&#43;&#43;11中的内存模型上篇 - 内存模型基础</title>
      <link>https://www.codedump.info/zh/post/20191214-cxx11-memory-model-1/</link>
      <pubDate>Sat, 14 Dec 2019 10:10:15 +0800</pubDate>
      <guid>https://www.codedump.info/zh/post/20191214-cxx11-memory-model-1/</guid>
      <description>&lt;p&gt;前段时间花了些精力研究C++11引入的内存模型相关的操作，于是把相关的知识都学习了一下，将这个学习过程整理为两篇文档，这是第一篇，主要分析内存模型的一些基础概念，第二篇展开讨论C++11相关的操作。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;cpu架构的演进&#34;&gt;&#xA;  CPU架构的演进&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#cpu%e6%9e%b6%e6%9e%84%e7%9a%84%e6%bc%94%e8%bf%9b&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;早期的CPU，CPU之间能共享访问的只有内存，此时的结构大体如图：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;memory&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-1/memory.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; memory &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;随着硬件技术的发展，内存的访问已经跟不上CPU的执行速度，此时内存反而变成了瓶颈。为了加速读写速度，每个CPU也都有自己内部才能访问的缓存，结构变成了这样：&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;multicore&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-1/multicore.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; multicore &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;其中：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;有多个CPU处理器，每个CPU处理器内部又有多个核心。&lt;/li&gt;&#xA;&lt;li&gt;存在只能被一个CPU核心访问的L1 cache。&lt;/li&gt;&#xA;&lt;li&gt;存在只能被一个CPU处理器的多个核心访问的L2 cache。&lt;/li&gt;&#xA;&lt;li&gt;存在能被所有CPU处理器都能访问到的L3 cache以及内存。&lt;/li&gt;&#xA;&lt;li&gt;L1 cache、L2 cache、L3 cache的容量空间依次变大，但是访问速度依次变慢。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;当CPU结构发生变化，增加了只能由内部才能访问的缓存之后，一些在旧架构上不会出现的问题，在新的架构上就会出现。而本篇的主角内存模型（memory model），其作用就是规定了各种不同的访问共享内存的方式，不同的内存模型，既需要编译器的支持，也需要硬件CPU的支持。&lt;/p&gt;&#xA;&lt;p&gt;我们从一个最简单的多线程访问变量问题谈起。&lt;/p&gt;&#xA;&lt;h1 class=&#34;heading&#34; id=&#34;简单的多线程访问数据问题&#34;&gt;&#xA;  简单的多线程访问数据问题&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e7%ae%80%e5%8d%95%e7%9a%84%e5%a4%9a%e7%ba%bf%e7%a8%8b%e8%ae%bf%e9%97%ae%e6%95%b0%e6%8d%ae%e9%97%ae%e9%a2%98&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;假设在程序执行之前，A=B=0，有两个线程同时分别执行如下的代码：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;线程1&lt;/th&gt;&#xA;          &lt;th&gt;线程2&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;1. A=1&lt;/td&gt;&#xA;          &lt;td&gt;3. B=2&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2. print(B)&lt;/td&gt;&#xA;          &lt;td&gt;4. print(A)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;问上述程序的执行结果如何？&lt;/p&gt;&#xA;&lt;p&gt;这个问题是一个简单的排列组合问题，其结果有：&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;2（先选择A或B输出）* 2（输出修改前还是之后的结果）* 1（前面第一步选择了一个变量之后，现在只能选剩下的变量）* 2（输出修改前还是之后的结果） = 8&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;p&gt;其可能的结果包括：(0,0)、(1,0)、(0,2)、(1,2)、(0,1)、(2,0)、(2,1)。（这里只有7个结果，是因为有两个(0,0)，所以少了一个）。&lt;/p&gt;&#xA;&lt;p&gt;由于多个线程交替执行，可能有以下几种结果，下面来分别解析。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;两个线程依次执行&#34;&gt;&#xA;  两个线程依次执行&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%b8%a4%e4%b8%aa%e7%ba%bf%e7%a8%8b%e4%be%9d%e6%ac%a1%e6%89%a7%e8%a1%8c&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;最简单的情况，就是这两个线程依次执行，即一个线程执行完毕之后再执行另一个线程的指令，这种情况下有两种可能：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;1-&amp;gt;2-&amp;gt;3-&amp;gt;4&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这种情况先执行完毕线程1，再执行线程2，最后输出的结果是(0,1)。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;sc1&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-1/sc1.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; sc1 &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;3-&amp;gt;4-&amp;gt;1-&amp;gt;2&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这种情况先执行完毕线程2，再执行线程1，最后输出的结果是(0,2)。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;sc2&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-1/sc2.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; sc2 &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;两个线程交替执行&#34;&gt;&#xA;  两个线程交替执行&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%b8%a4%e4%b8%aa%e7%ba%bf%e7%a8%8b%e4%ba%a4%e6%9b%bf%e6%89%a7%e8%a1%8c&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;这样情况下，先执行的可能是线程1或者线程2，来看线程1先执行的情况。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;1-&amp;gt;3-&amp;gt;2-&amp;gt;4&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这种情况下的输出是（2,1）。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;sc3&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-1/sc3.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; sc3 &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;1-&amp;gt;3-&amp;gt;4-&amp;gt;2&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;这种情况下的输出是（1,2）。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&#xA;&lt;figure class=&#34;&#34;&gt;&#xA;&#xA;    &lt;div&gt;&#xA;        &lt;img loading=&#34;lazy&#34; alt=&#34;sc4&#34; src=&#34;https://www.codedump.info/media/imgs/20191214-cxx11-memory-model-1/sc4.png&#34; &gt;&#xA;    &lt;/div&gt;&#xA;&#xA;    &#xA;    &lt;div class=&#34;caption-container&#34;&gt;&#xA;        &lt;figcaption&gt; sc4 &lt;/figcaption&gt;&#xA;    &lt;/div&gt;&#xA;    &#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;以上是第一条指令先执行线程1执行的情况，同样地也有先执行线程2指令的情况（3-1-&amp;gt;4-&amp;gt;2和3-&amp;gt;1-&amp;gt;2-4），这里不再列出，有兴趣的读者可以自行画图理解。&lt;/p&gt;&#xA;&lt;h2 class=&#34;heading&#34; id=&#34;不可能出现的情况&#34;&gt;&#xA;  不可能出现的情况&#xA;  &lt;a class=&#34;anchor&#34; href=&#34;#%e4%b8%8d%e5%8f%af%e8%83%bd%e5%87%ba%e7%8e%b0%e7%9a%84%e6%83%85%e5%86%b5&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;除了以上的情况之外，还有一种可能是输出(0,0)，但是这种输出在一般情况下不可能出现（我们接下来会解释什么情况下可能出现），下面来做解释。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
