<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>硬件原子指令 on Dorkytiger 的小屋</title>
    <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/</link>
    <description>Recent content in 硬件原子指令 on Dorkytiger 的小屋</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <atom:link href="https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>总线锁（Lock Asserted）与 缓存锁（Cache Locking）</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/01-cache-locking/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/01-cache-locking/</guid>
      <description>&lt;p&gt;这个问题问到了并发编程的根基，也是理解无锁编程（Lock-Free）和内存模型（Memory Model）的前提。为了让你直观理解，我们先从“为什么冲突”说起，再深入到硬件是如何“劝架”的。&lt;/p&gt;
&lt;h3 id=&#34;1-为什么多核-cpu-同时改一个内存会冲突&#34;&gt;1. 为什么多核 CPU 同时改一个内存会冲突？&lt;/h3&gt;
&lt;p&gt;先看一个C++经典场景：两个线程分别在不同核心上执行 &lt;code&gt;global_var++&lt;/code&gt;（读-改-写操作）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;冲突的本质不是“同时写入”，而是“基于过期数据的写入”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了提速，每个CPU核心都有自己的&lt;strong&gt;L1/L2缓存&lt;/strong&gt;。核心操作数据时，先在缓存里改，之后再同步回主存。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时刻1&lt;/strong&gt;：核心A和核心B都读取 &lt;code&gt;global_var = 10&lt;/code&gt; 到各自的缓存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时刻2&lt;/strong&gt;：核心A计算 &lt;code&gt;10+1=11&lt;/code&gt;，将 &lt;code&gt;11&lt;/code&gt; 写回主存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时刻3&lt;/strong&gt;：核心B&lt;strong&gt;依然拿着过期的10&lt;/strong&gt;，计算 &lt;code&gt;10+1=11&lt;/code&gt;，将 &lt;code&gt;11&lt;/code&gt; 写回主存。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;：明明加了两次，结果却是11（应该是12）。这就是经典的&lt;strong&gt;缓存一致性问题&lt;/strong&gt;。如果不加锁，最终结果取决于谁最后写（丢失了A的写入）。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-硬件如何锁住-总线锁lock-asserted&#34;&gt;2. 硬件如何“锁住”？—— 总线锁（Lock Asserted）&lt;/h3&gt;
&lt;p&gt;在最早期的多核CPU（或者访问未缓存的内存地址）中，硬件使用粗暴但有效的办法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;：CPU在执行特定指令时，会在其前端总线（FSB）上拉高一个名为 &lt;strong&gt;LOCK#&lt;/strong&gt; 的引脚信号（即断言&lt;code&gt;Lock Asserted&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;演示图（概念）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ 核心A ] ----( 请求锁总线 )---&amp;gt; [ 总线仲裁器 ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ 核心B ] ----( 等待... )------&amp;gt; [ 总线仲裁器 ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              v
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        +--------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        |  LOCK# 信号  |  &amp;lt;--- 只有持有者能访问主存
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        +--------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ 核心A ] ----&amp;gt; 执行 read-modify-write (原子性) ----&amp;gt; 释放 LOCK#
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ 核心B ] &amp;lt;---- 获得总线使用权 &amp;lt;---- 继续执行
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>x86 架构下的核心兵器：`LOCK` 前缀指令</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/02-lock-prefix/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/02-lock-prefix/</guid>
      <description>&lt;p&gt;&lt;code&gt;LOCK&lt;/code&gt;前缀就像给指令挂上了“原子手雷”，而&lt;code&gt;cmpxchg&lt;/code&gt;和&lt;code&gt;xadd&lt;/code&gt;则是两种不同战术的手雷。&lt;/p&gt;
&lt;p&gt;我们先拆解&lt;strong&gt;硬件行为&lt;/strong&gt;，再看&lt;strong&gt;汇编形态&lt;/strong&gt;，最后看&lt;strong&gt;C++如何映射&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-灵魂拷问lock-前缀到底对cpu做了什么&#34;&gt;1. 灵魂拷问：&lt;code&gt;LOCK&lt;/code&gt; 前缀到底对CPU做了什么？&lt;/h3&gt;
&lt;p&gt;当你在汇编指令前加上 &lt;code&gt;LOCK&lt;/code&gt;，CPU会执行以下&lt;strong&gt;硬件契约&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;断言总线锁或缓存锁&lt;/strong&gt;（我们上节课讲的，优先缓存锁）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存屏障（Memory Barrier）&lt;/strong&gt;：&lt;code&gt;LOCK&lt;/code&gt; 隐式包含&lt;strong&gt;全屏障（Full Barrier）&lt;/strong&gt;，它会强制：
&lt;ul&gt;
&lt;li&gt;将CPU核心的&lt;strong&gt;写缓冲区（Store Buffer）&lt;/strong&gt; 全部冲刷到缓存。&lt;/li&gt;
&lt;li&gt;确保这条指令之前的所有读写操作都&lt;strong&gt;全局可见&lt;/strong&gt;，之后的操作&lt;strong&gt;不会提前&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原子性保证&lt;/strong&gt;：在读-改-写期间，&lt;strong&gt;不允许其他核心/硬件设备&lt;/strong&gt;（如DMA）访问该内存地址。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：&lt;code&gt;LOCK&lt;/code&gt; 不是“锁住变量”，而是&lt;strong&gt;锁住对那个内存地址的访问路径&lt;/strong&gt;，保证读-改-写是一个不可分割的硬件事务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-核心兵器一xadd--原子自增自减的特快专列&#34;&gt;2. 核心兵器一：&lt;code&gt;xadd&lt;/code&gt; —— 原子自增/自减的“特快专列”&lt;/h3&gt;
&lt;h3 id=&#34;汇编原型&#34;&gt;汇编原型&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;lock&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;xadd&lt;/span&gt; [&lt;span style=&#34;color:#66d9ef&#34;&gt;mem&lt;/span&gt;], &lt;span style=&#34;color:#66d9ef&#34;&gt;reg&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;; 等价于：先交换，再相加
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 功能：将 [mem] 的值 与 reg 的值交换，然后将两者之和存回 [mem]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 返回值（旧值） 存放在 reg 中
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;执行过程3步原子完成&#34;&gt;执行过程（3步原子完成）&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;初始: [mem] = 10, reg (eax) = 1
1. 硬件原子性读取 [mem] 的值（10）到内部临时寄存器
2. 将 reg（1）写入 [mem]（此时 [mem]=1，但这是内部中间态）
3. 计算 10 + 1 = 11，写回 [mem]
最终: [mem]=11, reg=10  (reg 拿到了旧值)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;典型应用场景&#34;&gt;典型应用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高性能统计计数器&lt;/strong&gt;（网络包计数、QPS统计）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无锁栈的 &lt;code&gt;pop&lt;/code&gt; 操作&lt;/strong&gt;（减少引用计数）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;c-映射你几乎不会直接写汇编但要知道底层&#34;&gt;C++ 映射（你几乎不会直接写汇编，但要知道底层）&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;atomic&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;atomic&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; counter{&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// C++ 的 fetch_add 在 x86 上编译为 lock xadd
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; old_value &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; counter.fetch_add(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;memory_order_acq_rel);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// old_value 拿到增加前的值，counter 原子 +1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;反汇编查看（GCC/Clang）&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>ARM 架构下的核心兵器：LL/SC (Link-Load / Store-Conditional)</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/03-arm-llsc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/03-arm-llsc/</guid>
      <description>&lt;p&gt;这个问题切中了移动端和服务器端底层设计的&lt;strong&gt;根本分歧&lt;/strong&gt;。很多从x86转到ARM的开发者，第一反应就是：&amp;ldquo;为什么ARM没有&lt;code&gt;lock cmpxchg&lt;/code&gt;？&amp;quot;。&lt;/p&gt;
&lt;p&gt;答案很哲学：&lt;strong&gt;x86是复杂（CISC）的&amp;quot;霸道总裁&amp;rdquo;，而ARM是精简（RISC）的&amp;quot;优雅绅士&amp;quot;&lt;/strong&gt;。前者直接给你原子指令，后者给你&lt;strong&gt;两条指令&lt;/strong&gt;让你自己组合，以此换取极致的灵活性。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-为什么手机芯片arm不直接用-cas&#34;&gt;1. 为什么手机芯片（ARM）不直接用 CAS？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;直接原因：CAS（Compare-And-Swap）是一个&amp;quot;读-改-写&amp;quot;三合一的巨无霸指令。&lt;/strong&gt;
在RISC哲学里，一条指令只做一件事。如果强行在ARM里塞入&lt;code&gt;cmpxchg&lt;/code&gt;，会：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;增加晶体管开销&lt;/strong&gt;：需要复杂的微码控制器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;破坏多核扩展性&lt;/strong&gt;：未来的ARM可能几百个核，硬件的CAS逻辑会变成瓶颈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功耗太高&lt;/strong&gt;：手机对功耗极其敏感，&lt;code&gt;cmpxchg&lt;/code&gt;需要持续锁住总线等待结果，而ARM的LL/SC允许在失败时&lt;strong&gt;提前释放&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;根本区别在于&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;x86的CAS&lt;/strong&gt;：你告诉CPU &amp;ldquo;你要么成功，要么失败&amp;rdquo;，CPU内部硬扛所有冲突。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ARM的LL/SC&lt;/strong&gt;：CPU告诉你 &amp;ldquo;我给你做个标记，你试着改，如果我发现有人动过，我就让你重试&amp;rdquo;——&lt;strong&gt;把&amp;quot;判断冲突&amp;quot;的主动权交给软件&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-核心兵器拆解llload-link与-scstore-conditional&#34;&gt;2. 核心兵器拆解：LL（Load-Link）与 SC（Store-Conditional）&lt;/h3&gt;
&lt;p&gt;这是ARM体系（以及RISC-V、PowerPC）的原子操作基石。&lt;/p&gt;
&lt;h4 id=&#34;指令原型armv8-示例&#34;&gt;指令原型（ARMv8 示例）&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;; 假设我们要实现原子自增: *ptr += 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;1.&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;LL&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, [&lt;span style=&#34;color:#66d9ef&#34;&gt;x1&lt;/span&gt;]      &lt;span style=&#34;color:#75715e&#34;&gt;; Load-Link: 将 x1 地址的值读入 x0，并打上&amp;#34;独占标记&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;2.&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;ADD&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;#1    ; 普通加法（此时还没写回）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;3.&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;SC&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, [&lt;span style=&#34;color:#66d9ef&#34;&gt;x1&lt;/span&gt;]      &lt;span style=&#34;color:#75715e&#34;&gt;; Store-Conditional: 尝试把 x0 写入 x1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                       &lt;span style=&#34;color:#75715e&#34;&gt;; 如果成功，x0=0；如果失败（独占标记丢失），x0=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;4.&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;CBNZ&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;x0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;retry&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;; 如果 x0 != 0，跳回重试
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;硬件执行流程独占监视器机制&#34;&gt;硬件执行流程（独占监视器机制）&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[ 核心A ]                           [ 核心B ]                    [ 全局监视器 ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |                                    |                              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |--- 1. LL (读取地址P) --------------&amp;gt;| ---------------------------&amp;gt; | 记录: 核心A 正在监视 地址P
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |   (核心A: 拿到数据10)              |                              | (Exclusive Monitor 置位)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |                                    |                              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |   (此时核心A做加法,但没写)          |--- 对同一地址P 执行写操作 --&amp;gt; | 检测到冲突!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |                                    | (核心B: 写入11)             | 标记: 核心A 的监视失效
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |                                    |                              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |--- 3. SC (尝试写回11) -------------&amp;gt;| ---------------------------&amp;gt; | 检查: 核心A 的监视还在吗?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |   (返回失败: x0=1)                 |                              | 回复: 不在 (已失效)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |                                    |                              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    |--- 4. 检测失败 -&amp;gt; 跳转重试        |                              |
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id=&#34;3-硬件独占监视器exclusive-monitor的工作原理&#34;&gt;3. 硬件独占监视器（Exclusive Monitor）的工作原理&lt;/h3&gt;
&lt;p&gt;这是LL/SC的灵魂。它不是锁，而是一个&lt;strong&gt;监听状态的触发器&lt;/strong&gt;。分为两级：&lt;/p&gt;</description>
    </item>
    <item>
      <title>硬件层面的ABA问题</title>
      <link>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/04-aba-basics/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/c-plus-plus/c-plus-plus-concurrent-programming/01-hardware-atomics/04-aba-basics/</guid>
      <description>&lt;p&gt;这个问题触及了&lt;strong&gt;无锁编程最隐蔽的陷阱&lt;/strong&gt;。很多人以为ABA只是理论概念，但在&lt;strong&gt;内存复用&lt;/strong&gt;的场景下，它会导致&lt;strong&gt;内存损坏&lt;/strong&gt;甚至&lt;strong&gt;安全漏洞&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们先从&lt;strong&gt;硬件视角&lt;/strong&gt;看为什么CPU会被骗，再深入&lt;strong&gt;实际崩溃场景&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-硬件层面的失忆症&#34;&gt;1. 硬件层面的&amp;quot;失忆症&amp;quot;&lt;/h3&gt;
&lt;h3 id=&#34;场景重现用-cmpxchg&#34;&gt;场景重现（用 &lt;code&gt;cmpxchg&lt;/code&gt;）&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;初始状态: 共享指针 ptr 指向 内存块A (地址 0x1000)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[时刻1] 线程1 读取 ptr → 0x1000 (期望值)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[时刻2] 线程2 执行:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ptr 从 0x1000 → 0x2000 (释放A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ptr 从 0x2000 → 0x1000 (重新分配，又指向A)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[时刻3] 线程1 执行 lock cmpxchg:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          比较: 当前 ptr (0x1000) == 期望值 (0x1000) → ✅ 相等!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          硬件认为&amp;#34;没人动过&amp;#34;，允许写入新值 0x3000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;硬件视角&lt;/strong&gt;：地址0x1000确实没变，CAS成功。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;现实视角&lt;/strong&gt;：0x1000指向的内存内容可能已经被&lt;strong&gt;完全改写&lt;/strong&gt;（线程2在复用期间修改了A的内容），但CAS毫不知情。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-为什么传统锁不会遇到aba&#34;&gt;2. 为什么传统锁不会遇到ABA？&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 使用互斥锁 (Mutex)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;mutex mtx;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;lock_guard&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;mutex&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; lock(mtx);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 临界区: 执行过程中，其他线程无法进入
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 所以不可能出现&amp;#34;A→B→A&amp;#34;的中间态
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键差异&lt;/strong&gt;：锁是&lt;strong&gt;排他性阻塞&lt;/strong&gt;，而CAS是&lt;strong&gt;乐观重试&lt;/strong&gt;。锁期间其他线程根本动不了数据，自然不会有ABA。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
