<?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 夜云泊</title>
    <link>https://lifeislife.cn/tags/%E6%80%BB%E7%BA%BF/</link>
    <description>feedId:57980998056508425+userId:73222296380546048 Recent content in 总线 on 夜云泊</description>
    <generator>Hugo -- 0.163.1</generator>
    <language>zh</language>
    <lastBuildDate>Sun, 08 May 2022 10:48:23 +0000</lastBuildDate>
    <atom:link href="https://lifeislife.cn/tags/%E6%80%BB%E7%BA%BF/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>计算机组成原理-存储与 IO 系统</title>
      <link>https://lifeislife.cn/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86-%E5%AD%98%E5%82%A8%E4%B8%8Eio%E7%B3%BB%E7%BB%9F/</link>
      <pubDate>Sun, 08 May 2022 10:48:23 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86-%E5%AD%98%E5%82%A8%E4%B8%8Eio%E7%B3%BB%E7%BB%9F/</guid>
      <description>&lt;h2 id=&#34;存储器&#34;&gt;存储器&lt;/h2&gt;
&lt;h3 id=&#34;存储器的层次结构&#34;&gt;存储器的层次结构&lt;/h3&gt;
&lt;h4 id=&#34;sramstatic-random-access-memory静态随机存取存储器&#34;&gt;SRAM（Static Random-Access Memory，静态随机存取存储器）&lt;/h4&gt;
&lt;p&gt;CPU 如果形容成人的大脑的话，那么 CPU Cache (高速缓存) 就好比人的记忆。它用的是 SRAM 芯片。&lt;/p&gt;
&lt;p&gt;SRAM 的“静态”的意思是，只要处于通电状态，里面的数据就保持存在，一旦断电，数据就会丢失。SRAM 里 1bit 数据需要 6-8 个晶体管，所以 SRAM 的存储密度不高，同样的物理空间，能够存的数据有限。因为其电路简单，访问速度非常快。&lt;/p&gt;
&lt;p&gt;在 CPU 里，通常会有 L1、L2、L3 这样三层高速缓存。每个 CPU 核心都有一块属于自己的 L1 高速缓存，通常分成指令缓存和数据缓存，分开存放 CPU 使用的指令和数据。&lt;/p&gt;
&lt;p&gt;L2 的 Cache 同样是每个 CPU 核心都有的，不过它往往不在 CPU 核心的内部。所以，L2 Cache 的访问速度会比 L1 稍微慢一些。而 L3Cache，则通常是多个 CPU 核心共用的，尺寸会更大一些，访问速度自然也就更慢一些。&lt;/p&gt;
&lt;p&gt;你可以把 CPU 中的 L1Cache 理解为我们的短期记忆，把 L2/L3Cache 理解成长期记忆，把内存当成我们拥有的书架或者书桌。当我们自己记忆中没有资料的时候，可以从书桌或者书架上拿书来翻阅。这个过程中就相当于，数据从内存中加载到 CPU 的寄存器和 Cache 中，然后通过“大脑”，也就是 CPU，进行处理和运算。&lt;/p&gt;
&lt;h4 id=&#34;dramdynamic-random-access-memory动态随机存取存储器&#34;&gt;DRAM（Dynamic Random Access Memory，动态随机存取存储器）&lt;/h4&gt;
&lt;p&gt;内存用的芯片和 Cache 有所不同，它用的是一种叫作 DRAM 的芯片，比起 SRAM 来说，它的密度更高，有更大的容量，而且它也比 SRAM 芯片便宜不少。&lt;/p&gt;
&lt;p&gt;DRAM 被称为“动态”存储器，是因为 DRAM 需要靠不断地“刷新”，才能保持数据被存储起来。DRAM 的一个比特，只需要一个晶体管和一个电容就能存储。所以，DRAM 在同样的物理空间下，能够存储的数据也就更多，也就是存储的“密度”更大。但是，因为数据是存储在电容里的，电容会不断漏电，所以需要定时刷新充电，才能保持数据不丢失。DRAM 的数据访问电路和刷新电路都比 SRAM 更复杂，所以访问延时也就更长。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081652018.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081652018.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;从 Cache、内存，到 SSD 和 HDD 硬盘，一台现代计算机中，就用上了所有这些存储器设备。其中，容量越小的设备速度越快，而且，CPU 并不是直接和每一种存储器设备打交道，而是每一种存储器设备，只和它相邻的存储设备打交道。比如，CPUCache 是从内存里加载而来的，或者需要写回内存，并不会直接写回数据到硬盘，也不会直接从硬盘加载数据到 CPUCache 中，而是先加载到内存，再从内存加载到 Cache 中。&lt;/p&gt;
&lt;p&gt;这样，各个存储器只和相邻的一层存储器打交道，并且随着一层层向下，存储器的容量逐层增大，访问速度逐层变慢，而单位存储成本也逐层下降，也就构成了我们日常所说的存储器层次结构。&lt;/p&gt;
&lt;h2 id=&#34;缓存&#34;&gt;缓存&lt;/h2&gt;
&lt;h3 id=&#34;cpu-cache&#34;&gt;CPU cache&lt;/h3&gt;
&lt;h3 id=&#34;高速缓存&#34;&gt;高速缓存&lt;/h3&gt;
&lt;p&gt;缓存不是 CPU 的专属功能，可以把它当成一种策略，任何时候想要增加数据传输性能，都可以通过加一层缓存试试。&lt;/p&gt;
&lt;p&gt;存储器层次结构的中心思想是，对于每个$k$，位于$k$层的更快更小的存储设备作为位于$k+1$层的更大更慢的存储设备的缓存。&lt;a href=&#34;#%E5%AD%98%E5%82%A8%E5%99%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E4%B8%AD%E5%9F%BA%E6%9C%AC%E7%9A%84%E7%BC%93%E5%AD%98%E5%8E%9F%E7%90%86&#34;&gt;下图&lt;/a&gt;展示了存储器层次结构中缓存的一般性概念。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711145943.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711145943.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div id=&#34;存储器层次结构中基本的缓存原理&#34;&gt;&lt;/div&gt;
&lt;p&gt;数据总是以块&lt;code&gt;block&lt;/code&gt;为单位，在层与层之间来回复制。&lt;/p&gt;
&lt;p&gt;说回高速缓存，按照摩尔定律，CPU 的访问速度每 18 个月便会翻一翻，相当于每年增长 60%。内存的访问速度虽然不断增长，却远没有那么快，每年只增长 7% 左右。这样就导致 CPU 性能和内存访问的差距不断拉大。为了弥补两者之间差异，现代 CPU 引入了&lt;strong&gt;高速缓存&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220708092012.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220708092012.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;CPU 的读（load）实质上就是从缓存中读取数据到寄存器（register）里，在多级缓存的架构中，如果缓存中找不到数据（Cache miss），就会层层读取二级缓存三级缓存，一旦所有的缓存里都找不到对应的数据，就要去内存里寻址了。寻址到的数据首先放到寄存器里，其副本会驻留到 CPU 的缓存中。&lt;/p&gt;
&lt;p&gt;CPU 的写（store）也是针对缓存作写入。并不会直接和内存打交道，而是通过某种机制实现数据从缓存到内存的写回（write back）。&lt;/p&gt;
&lt;p&gt;缓存到底如何与 CPU 和主存数据交换的？CPU 如何从缓存中读写数据的？缓存中没有读的数据，或者缓存写满了怎么办？我们先从 CPU 如何读取数据说起。&lt;/p&gt;
&lt;h4 id=&#34;缓存读取&#34;&gt;缓存读取&lt;/h4&gt;
&lt;p&gt;CPU 发起一个读取请求后，返回的结果会有如下几种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存命中 (cache hit)
要读取的数据刚好在缓存中，叫做&lt;strong&gt;缓存命中&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;缓存不命中 (cache miss)
发送缓存不命中，缓存就得执行一直&lt;strong&gt;放置策略&lt;/strong&gt;(placement policy)，比如 LRU。来决定从主存中取出的数据放到哪里。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;强制性不命中&lt;/strong&gt;(compulsory miss)/冷不命中(cold miss)：缓存中没有要读取的数据，需要从主存读取数据，并将数据放入缓存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;冲突不命中&lt;/strong&gt;(conflict miss)：缓存中有要读的数据，在采取放置策略时，从主存中取数据放到缓存时发生了冲突，这叫做冲突不命中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;高速缓存存储器组织结构&#34;&gt;高速缓存存储器组织结构&lt;/h4&gt;
&lt;p&gt;整个 Cache 被划分为 1 个或多个&lt;strong&gt;组&lt;/strong&gt; (Set)，$S$ 表示组的个数。每个组包含 1 个或多个&lt;strong&gt;缓存行&lt;/strong&gt;(Cache line)，$E$ 表示一个组中缓存行的行数。每个缓存行由三部分组成：&lt;strong&gt;有效位&lt;/strong&gt;(valid)，&lt;strong&gt;标记位&lt;/strong&gt;（tag），&lt;strong&gt;数据块&lt;/strong&gt;（cache block）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有效位：该位等于 1，表示这个行数据有效。&lt;/li&gt;
&lt;li&gt;标记位：唯一的标识了存储在高速缓存中的块，标识目标数据是否存在当前的缓存行中。&lt;/li&gt;
&lt;li&gt;数据块：一部分内存数据的副本。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711171136.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711171136.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;Cache 的结构可以由元组$(S,E,B,m)$表示。不包括有效位和标记位。Cache 的大小为 $C=S \times E \times B$.&lt;/p&gt;
&lt;p&gt;接下来看看 Cache 是如何工作的，当 CPU 执行数据加载指令，从内存地址 A 读取数据时，根据存储器层次原理，如果 Cache 中保存着目标数据的副本，那么就立即将数据返回给 CPU。那么 Cache 如何知道自己保存了目标数据的副本呢？&lt;/p&gt;
&lt;p&gt;假设目标地址的数据长度为$m$位，这个地址被参数 $S$ 和 $B$ 分成了三个字段：&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711174416.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711174416.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;首先通过长度为$s$的&lt;strong&gt;组索引&lt;/strong&gt;，确定目标数据保存在哪一个组 (Set) 中，其次通过长度为$t$的&lt;strong&gt;标记&lt;/strong&gt;，确定在哪一行，需要注意的是此时有效位必须等于 1，最后根据长度为$b$的&lt;strong&gt;块偏移&lt;/strong&gt;，来确定目标数据在数据块中的确切位置。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Q：既然读取 Cache 第一步是组选择，为什么不用高位作为组索引，而使用中间的为作为组索引？
A：如果使用了高位作索引，那么一些连续的内存块就会映射到相同的高速缓存块。如图前四个块映射到第一个缓存组，第二个四个块映射到第二个组，依次类推。如果一个程序有良好的空间局部性，顺序扫描一个数组的元素，那么在任何时候，缓存中都只保存在一个块大小的数组内容。这样对缓存的使用率很低。相比而言，如果使用中间的位作为组索引，那么相邻的块总是映射到不同的组，图中的情况能够存放整个大小的数组片。


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711185819.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711185819.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5 id=&#34;直接映射高速缓存-direct-mapped-cache&#34;&gt;直接映射高速缓存 Direct Mapped Cache&lt;/h5&gt;
&lt;p&gt;根据每个组的缓存行数 $E$ 的不同，Cache 被分为不同的类。每个组只有一行$E=1$的高速缓存被称为&lt;strong&gt;直接映射高速缓存&lt;/strong&gt;(direct-mapped cache)。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711190845.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711190845.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;当一条加载指令指示 CPU 从主存地址 A 中读取一个字 w 时，会将该主存地址 A 发送到高速缓存中，则高速缓存会根据&lt;strong&gt;组选择&lt;/strong&gt;，&lt;strong&gt;行匹配&lt;/strong&gt;和&lt;strong&gt;字抽取&lt;/strong&gt;三步来判断地址 A 是否命中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;组选择&lt;/strong&gt;(set selection)：根据组索引值来确定属于哪一个组，如图中索引长度为 5 位，可以检索 32 个组 ($2^5=32$)。当$s=0$时，此时组选择的结果为&lt;code&gt;set 0&lt;/code&gt;，当$s=1$时，此时组选择的结果为&lt;code&gt;set 1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711191708.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711191708.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;行匹配 (line match)&lt;/strong&gt;：首先看缓存行的有效位，此时有效位为 1，表示当前数据有效。然后对比缓存行的标记&lt;code&gt;0110&lt;/code&gt;与地址中的标记&lt;code&gt;0110&lt;/code&gt;是否相等，如果相等，则表示目标数据在当前的缓存行中（缓存命中）。如果不一致或者有效位为 0，则表示目标数据不在当前的缓存行中（缓存不命中）。如果命中，就可以进行下一步字抽取。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192435.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192435.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字抽取 (word extraction)&lt;/strong&gt;：根据偏移量$b$确定目标数据的确切位置，通俗来说就是从数据块的什么位置开始抽取位置。如当偏移块等于&lt;code&gt;100&lt;/code&gt;时，表示目标数据起始地址位于字节 4 处。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192757.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192757.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;下面通过一个例子来解释清除这个过程。假设我们有一个直接映射高速缓存，描述为$(S,E,B,m) = (4,1,2,4)$。换句话说，高速缓存有 4 个组，每个组 1 行，每个数据块 2 个字节，地址长度为 4 位。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194256.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194256.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看出，8 个内存块，但只有 4 个高速缓存组，所以会有多个块映射到同一个高速缓存组中。例如，块 0 和块 4 都会被映射到组 0。&lt;/p&gt;
&lt;p&gt;下面我们来模拟当 CPU 执行一系列读的时候，高速缓存的执行情况，我们假设每次 CPU 读 1 个字节的字。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读地址 0(0000) 的字：&lt;/strong&gt;


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711193901.gif&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711193901.gif&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读地址 1(0001) 的字：&lt;/strong&gt;


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194838.gif&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194838.gif&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读地址 13(1101) 的字：&lt;/strong&gt;


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711195108.gif&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711195108.gif&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读地址 8(1000) 的字：&lt;/strong&gt;


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200054.gif&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200054.gif&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;读地址 0(0000) 的字：&lt;/strong&gt;


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200409.gif&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200409.gif&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h5 id=&#34;组相联高速缓存-set-associative-cache&#34;&gt;组相联高速缓存 Set Associative Cache&lt;/h5&gt;
&lt;p&gt;由于直接映射高速缓存的组中只有一行，所以容易发生冲突不命中。组相联高速缓存 (Set associative cache) 运行有多行缓存行。但是缓存行最大不能超过 $C/B$。&lt;/p&gt;
&lt;p&gt;如图一个组中包含了两行缓存行，这种我们称为 2 路相联高速缓存。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220712160513.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220712160513.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;组选择&lt;/strong&gt;：与直接映射高速缓存的组选择过程一样。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;行匹配&lt;/strong&gt;：因为一个组有多行，所以需要遍历所有行，找到一个有效位为 1，并且标记为与地址中的标记位相匹配的一行。如果找到了，表示缓存命中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字抽取&lt;/strong&gt;：根据偏移量$b$确定目标数据的确切位置，通俗来说就是从数据块的什么位置开始抽取位置。如当偏移块等于&lt;code&gt;100&lt;/code&gt;时，表示目标数据起始地址位于字节 4 处。&lt;/p&gt;
&lt;p&gt;如果不命中，那么就需要从主存中取出需要的数据块，但是将数据块放在哪一行缓存行呢？如果存在空行 ($valid=0$)，那就放到空行里。如果没有空行，就得选择一个非空行来替换，同时希望 CPU 不会很快引用这个被替换的行。这里介绍几个替换策略。&lt;/p&gt;
&lt;p&gt;最简单的方式就是随机选择一行来替换，其他复杂的方式就是利用局部性原理，使得接下来 CPU 引用替换的行概率最小。如&lt;/p&gt;
&lt;h3 id=&#34;缓存一致性协议-mesi&#34;&gt;缓存一致性协议 MESI&lt;/h3&gt;
&lt;h4 id=&#34;为什么需要缓存一致&#34;&gt;为什么需要缓存一致&lt;/h4&gt;
&lt;p&gt;目前主流电脑的 CPU 都是多核心的，多核心的有点就是在不能提升 CPU 主频后，通过增加核心来提升 CPU 吞吐量。每个核心都有自己的 L1 Cache 和 L2 Cache，只是共用 L3 Cache 和主内存。每个核心操作是独立的，每个核心的 Cache 就不是同步更新的，这样就会带来缓存一致性（Cache Coherence）的问题。&lt;/p&gt;
&lt;p&gt;举个例子，如图：


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205291536919.gif&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205291536919.gif&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;有 2 个 CPU，主内存里有个变量&lt;code&gt;x=0&lt;/code&gt;。CPU A 中有个需要将变量&lt;code&gt;x&lt;/code&gt;加&lt;code&gt;1&lt;/code&gt;。CPU A 就将变量&lt;code&gt;x&lt;/code&gt;加载到自己的缓存中，然后将变量&lt;code&gt;x&lt;/code&gt;加&lt;code&gt;1&lt;/code&gt;。因为此时 CPU A 还未将缓存数据写回主内存，CPU B 再读取变量&lt;code&gt;x&lt;/code&gt;时，变量&lt;code&gt;x&lt;/code&gt;的值依然是&lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里的问题就是所谓的缓存一致性问题，因为 CPU A 的缓存与 CPU B 的缓存是不一致的。&lt;/p&gt;
&lt;h4 id=&#34;如何解决缓存一致性问题&#34;&gt;如何解决缓存一致性问题&lt;/h4&gt;
&lt;h5 id=&#34;通过在总线加-lock-锁的方式&#34;&gt;通过在总线加 LOCK 锁的方式&lt;/h5&gt;
&lt;p&gt;在锁住总线上加一个 LOCK 标识，CPU A 进行读写操作时，锁住总线，其他 CPU 此时无法进行内存读写操作，只有等解锁了才能进行操作。&lt;/p&gt;
&lt;p&gt;该方式因为锁住了整个总线，所以效率低。&lt;/p&gt;
&lt;h5 id=&#34;缓存一致性协议-mesi-1&#34;&gt;缓存一致性协议 MESI&lt;/h5&gt;
&lt;p&gt;该方式对单个缓存行的数据进行加锁，不会影响到内存其他数据的读写。&lt;/p&gt;
&lt;p&gt;在学习 MESI 协议之前，简单了解一下总线嗅探机制（Bus Snooping）。要对自己的缓存加锁，需要通知其他 CPU，多个 CPU 核心之间的数据传播问题。最常见的一种解决方案就是总线嗅探。&lt;/p&gt;
&lt;p&gt;这个策略，本质上就是把所有的读写请求都通过总线广播给所有的 CPU 核心，然后让各个核心去“嗅探”这些请求，再根据本地的情况进行响应。MESI 就是基于总线嗅探机制的缓存一致性协议。&lt;/p&gt;
&lt;p&gt;MESI 协议的由来是对 Cache Line 的四个不同的标记，分别是：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:50px&#34;&gt;状态&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:100px&#34;&gt;状态&lt;/div&gt;&lt;/th&gt;
					&lt;th&gt;&lt;div style=&#34;width:200px&#34;&gt;描述&lt;/div&gt;&lt;/th&gt;
					&lt;th&gt;&lt;div style=&#34;width:200px&#34;&gt;监听任务&lt;/div&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Modified&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;已修改&lt;/td&gt;
					&lt;td&gt;该 Cache Line 有效，数据被修改了，和内存中的数据不一致，数据只存在于本 Cache 中&lt;/td&gt;
					&lt;td&gt;Cache Line 必须时刻监听所有试图读该 Cache Line 相对于主存的操作，这种操作必须在缓存将该 Cache Line 写回主存并将状态改为 S 状态之前，被延迟执行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Exclusive&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;独享，互斥&lt;/td&gt;
					&lt;td&gt;该 Cache Line 有效，数据和内存中的数据一直，数据只存在于本 Cache&lt;/td&gt;
					&lt;td&gt;Cache Line 必须监听其他缓存读主存中该 Cache Line 的操作，一旦有这种操作，该 Cache Line 需要改为 S 状态&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Shared&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;共享的&lt;/td&gt;
					&lt;td&gt;该 Cache Line 有效，数据和内存中的数据一直，数据存在于很多个 Cache 中&lt;/td&gt;
					&lt;td&gt;Cache Line 必须监听其他  Cache Line 使该 Cache Line 无效或者独享该 Cache Line 的请求，并将 Cache Line 改为 I 状态&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Invalid&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;无效的&lt;/td&gt;
					&lt;td&gt;该 Cache Line 无效&lt;/td&gt;
					&lt;td&gt;无&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;整个 MESI 的状态，可以用一个有限状态机来表示它的状态流转。需要注意的是，对于不同状态触发的事件操作，可能来自于当前 CPU 核心，也可能来自总线里其他 CPU 核心广播出来的信号。我把各个状态之间的流转用表格总结了一下：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;当前状态&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;事件&lt;/div&gt;&lt;/th&gt;
					&lt;th&gt;&lt;div style=&#34;width:300px,center&#34;&gt;行为&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;下个状态&lt;/div&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Read&lt;/td&gt;
					&lt;td&gt;从 Cache 中读，状态不变&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Write&lt;/td&gt;
					&lt;td&gt;修改 cache 数据，状态不变&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Read&lt;/td&gt;
					&lt;td&gt;这行数据被写到内存中，使其他核能使用到最新数据，状态变为 S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Write&lt;/td&gt;
					&lt;td&gt;这行数据被写入内存中，其他核可以获取到最新数据，由于其他 CPU 修改该条数据，则本地 Cache 变为 I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;当前状态&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;事件&lt;/div&gt;&lt;/th&gt;
					&lt;th&gt;&lt;div style=&#34;width:200px,center&#34;&gt;行为&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;下个状态&lt;/div&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;E&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Read&lt;/td&gt;
					&lt;td&gt;从 Cache 中读，状态不变&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;E&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;E&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Write&lt;/td&gt;
					&lt;td&gt;修改数据，状态改为 M&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;E&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Read&lt;/td&gt;
					&lt;td&gt;数据和其他 CPU 共享，变为 S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;E&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Write&lt;/td&gt;
					&lt;td&gt;数据被修改，本地缓存失效，变为 I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;当前状态&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;事件&lt;/div&gt;&lt;/th&gt;
					&lt;th&gt;&lt;div style=&#34;width:200px,text-align: center&#34;&gt;行为&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;下个状态&lt;/div&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Read&lt;/td&gt;
					&lt;td&gt;从 Cache 中读，状态不变&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Write&lt;/td&gt;
					&lt;td&gt;修改数据，状态改为 M，其他 CPU 的 Cache Line 状态改为 I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Read&lt;/td&gt;
					&lt;td&gt;数据和其他 CPU 共享，状态不变&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Write&lt;/td&gt;
					&lt;td&gt;数据被修改，本地缓存失效，变为 I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;当前状态&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;事件&lt;/div&gt;&lt;/th&gt;
					&lt;th&gt;&lt;div style=&#34;width:200px,center&#34;&gt;行为&lt;/div&gt;&lt;/th&gt;
					&lt;th style=&#34;text-align: center&#34;&gt;&lt;div style=&#34;width:80px&#34;&gt;下个状态&lt;/div&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Read&lt;/td&gt;
					&lt;td&gt;1. 如果其他 CPU 没有这份数据，直接从内存中加载数据，状态变为 E；&lt;br&gt; 2. 如果其他 CPU 有这个数据，且 Cache Line 状态为 M，则先把 Cache Line 中的内容写回到主存。本地 Cache 再从内存中读取数据，这时两个 Cache Line 的状态都变为 S；&lt;br&gt;3. 如果其他 Cache Line 有这份数据，并且状态为 S 或者 E，则本地 Cache Line 从主存读取数据，并将这些 Cache Line 状态改为 S&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;E 或者 S&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Local Write&lt;/td&gt;
					&lt;td&gt;1. 先从内存中读取数据，如果其他 Cache Line 中有这份数据，且状态为 M，则现将数据更新到主存再读取，将 Cache Line 状态改为 M；&lt;br&gt; 2. 如果其他 Cache Line 有这份数据，且状态为 E 或者 S，则其他 Cache Line 状态改为 I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;M&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Read&lt;/td&gt;
					&lt;td&gt;数据和其他 CPU 共享，状态不变&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;S&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;Remote Write&lt;/td&gt;
					&lt;td&gt;数据被修改，本地缓存失效，变为 I&lt;/td&gt;
					&lt;td style=&#34;text-align: center&#34;&gt;I&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;内存&#34;&gt;内存&lt;/h2&gt;
&lt;p&gt;计算机有五大组成部分，分别是：运算器、控制器、存储器、输入设备和输出设备。而内存就是其中的存储器。我们的数据和指令都需要先放到内存中，然后再被 CPU 执行。&lt;/p&gt;
&lt;p&gt;操作系统中程序并不能直接访问物理内存，我们的内存需要被分成固定大小的页（Page），然后再通过&lt;strong&gt;虚拟内存地址（Virtual Address）到物理内存地址（Physical Address）的地址转换（Address Translation）&lt;/strong&gt;，才能到达实际存放数据的物理内存位置。而我们的程序看到的内存地址，都是虚拟内存地址。那么如何进行转换的呢？&lt;/p&gt;
&lt;h3 id=&#34;简单页表&#34;&gt;简单页表&lt;/h3&gt;
&lt;p&gt;最简单的方式，就是建立一张虚拟内存到物理内存的映射表，在计算机里叫做页表（Page Table）。页表这个地址转换的办法，会把一个内存地址分成页号（Directory）和偏移量（Offset）两个部分，是不是似曾相识，因为在前面的高速缓存里，缓存的结构也是这样的。&lt;/p&gt;
&lt;p&gt;以一个 32 位地址举例，高 20 位是虚拟页号，可以从虚拟页表中找到物理页号的信息，低 12 位是偏移量，可以准确获得物理地址。


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161640968.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161640968.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;总结一下，对于一个内存地址转换，其实就是这样三个步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把虚拟内存地址，切分成页号和偏移量的组合；&lt;/li&gt;
&lt;li&gt;从页表里面，查询出虚拟页号，对应的物理页号；&lt;/li&gt;
&lt;li&gt;直接拿物理页号，加上前面的偏移量，就得到了物理内存地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161645714.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161645714.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;但是这样的页表有个问题，它需要记录$2^{20}$个物理页表，这个存储关系，就好比一个 $2^{20}$大小的数组。一个页号是完整的 32 位的 4 字节（Byte），这样一个页表就需要 4MB 的空间。并且每个进程都会有这样一个页表，现代电脑正常都有成百上千个进程，如果用这样的页表肯定行不通的。&lt;/p&gt;
&lt;h3 id=&#34;多级页表&#34;&gt;多级页表&lt;/h3&gt;
&lt;p&gt;所以，&lt;strong&gt;在一个实际的程序进程里面，虚拟内存占用的地址空间，通常是两段连续的空间。而不是完全散落的随机的内存地址&lt;/strong&gt;。而多级页表，就特别适合这样的内存地址分布。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://zhuanlan.zhihu.com/p/357648933&#34;&gt;谈一谈内存管理，虚拟内存，多级页表 - 知乎&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;tlb&#34;&gt;TLB&lt;/h3&gt;
&lt;h3 id=&#34;内存保护---可执行空间保护&#34;&gt;内存保护 - 可执行空间保护&lt;/h3&gt;
&lt;h3 id=&#34;内存保护---地址空间布局随机化&#34;&gt;内存保护 - 地址空间布局随机化&lt;/h3&gt;
&lt;p&gt;Address Space Layout Randomization&lt;/p&gt;
&lt;h2 id=&#34;总线计算机内部的高速公路&#34;&gt;总线：计算机内部的高速公路&lt;/h2&gt;
&lt;p&gt;计算机由控制器、运算器、存储器、输入设备以及输出设备五大部分组成。CPU 所代表的控制器和运算器，要和存储器，也就是我们的主内存，以及输入和输出设备进行通信。那么计算机是用什么样的方式来完成，CPU 和内存、以及外部输入输出设备的通信呢？答案就是通过总线来通信。&lt;/p&gt;
&lt;p&gt;计算机里有不同的硬件设备，如果设备与设备之间都单独连接，那么就需要 N*N 的连线。那么怎么降低复杂度呢？与其让各个设备之间互相单独通信，不如我们去设计一个公用的线路。CPU 想要和什么设备通信，通信的指令是什么，对应的数据是什么，都发送到这个线路上；设备要向 CPU 发送什么信息呢，也发送到这个线路上。这个线路就好像一个高速公路，各个设备和其他设备之间，不需要单独建公路，只建一条小路通向这条高速公路就好了。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510203.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510203.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510711.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510711.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h3 id=&#34;三种线路和多总线架构&#34;&gt;三种线路和多总线架构&lt;/h3&gt;
&lt;p&gt;首先，CPU 和内存以及高速缓存通信的总线，这里面通常有两种总线。这种方式，我们称之为双独立总线（Dual Independent Bus，缩写为 DIB）。CPU 里，有一个快速的本地总线（Local Bus），以及一个速度相对较慢的前端总线（Front-side Bus）。&lt;/p&gt;
&lt;p&gt;现代的 CPU 里，通常有专门的高速缓存芯片。这里的高速本地总线，就是用来和高速缓存通信的。而前端总线，则是用来和主内存以及输入输出设备通信的。有时候，我们会把本地总线也叫作后端总线（Back-sideBus），和前面的前端总线对应起来。&lt;/p&gt;
&lt;p&gt;除了前端总线呢，我们常常还会听到 PCI 总线、I/O 总线或者系统总线（System Bus）。看到这么多总线的名字，你是不是已经有点晕了。这些名词确实容易混为一谈。其实各种总线的命名一直都很混乱，我们不如直接来看一看 CPU 的硬件架构图。对照图来看，一切问题就都清楚了。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081513938.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081513938.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;CPU 里面的北桥芯片，把我们上面说的前端总线，一分为二，变成了三个总线。我们的前端总线，其实就是系统总线。CPU 里面的内存接口，直接和系统总线通信，然后系统总线再接入一个 I/O 桥接器（I/OBridge）。这个 I/O 桥接器，一边接入了我们的内存总线，使得我们的 CPU 和内存通信；另一边呢，又接入了一个 I/O 总线，用来连接 I/O 设备。&lt;/p&gt;
&lt;p&gt;事实上，真实的计算机里，这个总线层面拆分得更细。根据不同的设备，还会分成独立的 PCI 总线、ISA 总线等等。


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081516341.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081516341.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;在物理层面，其实我们完全可以把总线看作一组“电线”。不过呢，这些电线之间也是有分工的，我们通常有三类线路。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据线（Data Bus），用来传输实际的数据信息，也就是实际上了公交车的“人”。&lt;/li&gt;
&lt;li&gt;地址线（Address Bus），用来确定到底把数据传输到哪里去，是内存的某个位置，还是某一个 I/O 设备。这个其实就相当于拿了个纸条，写下了上面的人要下车的站点。&lt;/li&gt;
&lt;li&gt;控制线（ControlBus），用来控制对于总线的访问。虽然我们把总线比喻成了一辆公交车。那么有人想要做公交车的时候，需要告诉公交车司机，这个就是我们的控制信号。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;尽管总线减少了设备之间的耦合，也降低了系统设计的复杂度，但同时也带来了一个新问题，那就是&lt;strong&gt;总线不能同时给多个设备提供通信功能&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们的总线是很多个设备公用的，那多个设备都想要用总线，我们就需要有一个机制，去决定这种情况下，到底把总线给哪一个设备用。这个机制，就叫作&lt;strong&gt;总线裁决&lt;/strong&gt;（Bus Arbitraction）&lt;/p&gt;
&lt;h2 id=&#34;硬盘&#34;&gt;硬盘&lt;/h2&gt;
&lt;h2 id=&#34;dma&#34;&gt;DMA&lt;/h2&gt;
&lt;p&gt;过去几年，计算机产业一直在为提升 I/O 设备的速度而努力，从机械硬盘 HDD 到固态硬盘 SSD，从 SATA 协议到 PCIE 协议，虽然速度都几十上百倍的增加，但是仍然不够快。因为相比于 CPU 基本都是 2GHz 的频率（每秒会有 20 亿次的操作），SSD 硬盘的 IOPS 的 2 万次操作就显得微不足道。&lt;/p&gt;
&lt;p&gt;如果我们对于 I/O 的操作，都是由 CPU 发出对应的指令，然后等待 I/O 设备完成操作之后返回，那 CPU 有大量的时间其实都是在等待 I/O 设备完成操作。特别是当传输的数据量比较大的时候，比如进行大文件复制，如果所有数据都要经过 CPU，实在是有点儿太浪费时间了。&lt;/p&gt;
&lt;p&gt;因此，计算机工程师们，就发明了&lt;strong&gt;DMA 技术&lt;/strong&gt;，也就是&lt;strong&gt;直接内存访问（Direct Memory Access）技术&lt;/strong&gt;，来减少 CPU 等待的时间。&lt;/p&gt;
&lt;h3 id=&#34;什么是-dma&#34;&gt;什么是 DMA&lt;/h3&gt;
&lt;p&gt;本质上，DMA 技术就是我们在主板上放一块&lt;strong&gt;独立的芯片&lt;/strong&gt;。在进行内存和 I/O 设备的数据传输的时候，我们不再通过 CPU 来控制数据传输，而直接通过 DMA 控制器（DMA Controller，简称 DMAC）。这块芯片，我们可以认为它其实就是一个&lt;strong&gt;协处理器&lt;/strong&gt;（Co-Processor）。&lt;/p&gt;
&lt;p&gt;DMAC 最有价值的地方体现在，当我们要传输的数据特别大、速度特别快，或者传输的数据特别小、速度特别慢的时候。&lt;/p&gt;
&lt;p&gt;比如说，我们用千兆网卡或者硬盘传输大量数据的时候，如果都用 CPU 来搬运的话，肯定忙不过来，所以可以选择 DMAC。而当数据传输很慢的时候，DMAC 可以等数据到齐了，再向 CPU 发起中断，让 CPU 去处理，而不是让 CPU 在那里忙等待。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202208152252016.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202208152252016.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先，CPU 还是作为一个主设备，向 DMAC 设备发起请求。这个请求，其实就是在 DMAC 里面修改配置寄存器。&lt;/li&gt;
&lt;li&gt;CPU 修改 DMAC 的配置的时候，会告诉 DMAC 这样几个信息：
&lt;ul&gt;
&lt;li&gt;源地址的初始值：数据要从哪里传输过来。如果我们要从内存里面写入数据到硬盘上，那么就是要读取的数据在内存里面的地址&lt;/li&gt;
&lt;li&gt;传输时候的地址增减方式：数据是从大的地址向小的地址传输，还是从小的地址往大的地址传输&lt;/li&gt;
&lt;li&gt;传输的数据长度：也就是我们一共要传输多少数据&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;设置完这些信息之后，DMAC 就会变成一个空闲的状态（Idle）。&lt;/li&gt;
&lt;li&gt;如果我们要从硬盘上往内存里面加载数据，这个时候，硬盘就会向 DMAC 发起一个数据传输请求。这个请求并不是通过总线，而是通过一个额外的连线。&lt;/li&gt;
&lt;li&gt;然后，我们的 DMAC 需要再通过一个额外的连线响应这个申请。&lt;/li&gt;
&lt;li&gt;DMAC 这个芯片，就向硬盘的接口发起要总线读的传输请求。数据就从硬盘里面，读到了 DMAC 的控制器里面。&lt;/li&gt;
&lt;li&gt;DMAC 再向我们的内存发起总线写的数据传输请求，把数据写入到内存里面。&lt;/li&gt;
&lt;li&gt;DMAC 会反复进行上面第 6、7 步的操作，直到 DMAC 的寄存器里面设置的数据长度传输完成。&lt;/li&gt;
&lt;li&gt;数据传输完成之后，DMAC 重新回到第 3 步的空闲状态。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以，整个数据传输的过程中，我们不是通过 CPU 来搬运数据，而是由 DMAC 这个芯片来搬运数据。但是 CPU 在这个过程中也是必不可少的。因为传输什么数据，从哪里传输到哪里，其实还是由 CPU 来设置的。这也是为什么，DMAC 被叫作 &lt;strong&gt;协处理器&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.bilibili.com/video/BV1cJ411K7HW?spm_id_from=333.999.0.0&#34;&gt;【硬件科普】电脑主板右下角的散热片下面究竟隐藏着什么？详解主板南桥芯片组的功能和作用_哔哩哔哩_bilibili&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="存储器">存储器</h2>
<h3 id="存储器的层次结构">存储器的层次结构</h3>
<h4 id="sramstatic-random-access-memory静态随机存取存储器">SRAM（Static Random-Access Memory，静态随机存取存储器）</h4>
<p>CPU 如果形容成人的大脑的话，那么 CPU Cache (高速缓存) 就好比人的记忆。它用的是 SRAM 芯片。</p>
<p>SRAM 的“静态”的意思是，只要处于通电状态，里面的数据就保持存在，一旦断电，数据就会丢失。SRAM 里 1bit 数据需要 6-8 个晶体管，所以 SRAM 的存储密度不高，同样的物理空间，能够存的数据有限。因为其电路简单，访问速度非常快。</p>
<p>在 CPU 里，通常会有 L1、L2、L3 这样三层高速缓存。每个 CPU 核心都有一块属于自己的 L1 高速缓存，通常分成指令缓存和数据缓存，分开存放 CPU 使用的指令和数据。</p>
<p>L2 的 Cache 同样是每个 CPU 核心都有的，不过它往往不在 CPU 核心的内部。所以，L2 Cache 的访问速度会比 L1 稍微慢一些。而 L3Cache，则通常是多个 CPU 核心共用的，尺寸会更大一些，访问速度自然也就更慢一些。</p>
<p>你可以把 CPU 中的 L1Cache 理解为我们的短期记忆，把 L2/L3Cache 理解成长期记忆，把内存当成我们拥有的书架或者书桌。当我们自己记忆中没有资料的时候，可以从书桌或者书架上拿书来翻阅。这个过程中就相当于，数据从内存中加载到 CPU 的寄存器和 Cache 中，然后通过“大脑”，也就是 CPU，进行处理和运算。</p>
<h4 id="dramdynamic-random-access-memory动态随机存取存储器">DRAM（Dynamic Random Access Memory，动态随机存取存储器）</h4>
<p>内存用的芯片和 Cache 有所不同，它用的是一种叫作 DRAM 的芯片，比起 SRAM 来说，它的密度更高，有更大的容量，而且它也比 SRAM 芯片便宜不少。</p>
<p>DRAM 被称为“动态”存储器，是因为 DRAM 需要靠不断地“刷新”，才能保持数据被存储起来。DRAM 的一个比特，只需要一个晶体管和一个电容就能存储。所以，DRAM 在同样的物理空间下，能够存储的数据也就更多，也就是存储的“密度”更大。但是，因为数据是存储在电容里的，电容会不断漏电，所以需要定时刷新充电，才能保持数据不丢失。DRAM 的数据访问电路和刷新电路都比 SRAM 更复杂，所以访问延时也就更长。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081652018.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081652018.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>从 Cache、内存，到 SSD 和 HDD 硬盘，一台现代计算机中，就用上了所有这些存储器设备。其中，容量越小的设备速度越快，而且，CPU 并不是直接和每一种存储器设备打交道，而是每一种存储器设备，只和它相邻的存储设备打交道。比如，CPUCache 是从内存里加载而来的，或者需要写回内存，并不会直接写回数据到硬盘，也不会直接从硬盘加载数据到 CPUCache 中，而是先加载到内存，再从内存加载到 Cache 中。</p>
<p>这样，各个存储器只和相邻的一层存储器打交道，并且随着一层层向下，存储器的容量逐层增大，访问速度逐层变慢，而单位存储成本也逐层下降，也就构成了我们日常所说的存储器层次结构。</p>
<h2 id="缓存">缓存</h2>
<h3 id="cpu-cache">CPU cache</h3>
<h3 id="高速缓存">高速缓存</h3>
<p>缓存不是 CPU 的专属功能，可以把它当成一种策略，任何时候想要增加数据传输性能，都可以通过加一层缓存试试。</p>
<p>存储器层次结构的中心思想是，对于每个$k$，位于$k$层的更快更小的存储设备作为位于$k+1$层的更大更慢的存储设备的缓存。<a href="#%E5%AD%98%E5%82%A8%E5%99%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E4%B8%AD%E5%9F%BA%E6%9C%AC%E7%9A%84%E7%BC%93%E5%AD%98%E5%8E%9F%E7%90%86">下图</a>展示了存储器层次结构中缓存的一般性概念。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711145943.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711145943.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div id="存储器层次结构中基本的缓存原理"></div>
<p>数据总是以块<code>block</code>为单位，在层与层之间来回复制。</p>
<p>说回高速缓存，按照摩尔定律，CPU 的访问速度每 18 个月便会翻一翻，相当于每年增长 60%。内存的访问速度虽然不断增长，却远没有那么快，每年只增长 7% 左右。这样就导致 CPU 性能和内存访问的差距不断拉大。为了弥补两者之间差异，现代 CPU 引入了<strong>高速缓存</strong>。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220708092012.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220708092012.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>CPU 的读（load）实质上就是从缓存中读取数据到寄存器（register）里，在多级缓存的架构中，如果缓存中找不到数据（Cache miss），就会层层读取二级缓存三级缓存，一旦所有的缓存里都找不到对应的数据，就要去内存里寻址了。寻址到的数据首先放到寄存器里，其副本会驻留到 CPU 的缓存中。</p>
<p>CPU 的写（store）也是针对缓存作写入。并不会直接和内存打交道，而是通过某种机制实现数据从缓存到内存的写回（write back）。</p>
<p>缓存到底如何与 CPU 和主存数据交换的？CPU 如何从缓存中读写数据的？缓存中没有读的数据，或者缓存写满了怎么办？我们先从 CPU 如何读取数据说起。</p>
<h4 id="缓存读取">缓存读取</h4>
<p>CPU 发起一个读取请求后，返回的结果会有如下几种情况：</p>
<ul>
<li>缓存命中 (cache hit)
要读取的数据刚好在缓存中，叫做<strong>缓存命中</strong>。</li>
<li>缓存不命中 (cache miss)
发送缓存不命中，缓存就得执行一直<strong>放置策略</strong>(placement policy)，比如 LRU。来决定从主存中取出的数据放到哪里。
<ul>
<li><strong>强制性不命中</strong>(compulsory miss)/冷不命中(cold miss)：缓存中没有要读取的数据，需要从主存读取数据，并将数据放入缓存。</li>
<li><strong>冲突不命中</strong>(conflict miss)：缓存中有要读的数据，在采取放置策略时，从主存中取数据放到缓存时发生了冲突，这叫做冲突不命中。</li>
</ul>
</li>
</ul>
<h4 id="高速缓存存储器组织结构">高速缓存存储器组织结构</h4>
<p>整个 Cache 被划分为 1 个或多个<strong>组</strong> (Set)，$S$ 表示组的个数。每个组包含 1 个或多个<strong>缓存行</strong>(Cache line)，$E$ 表示一个组中缓存行的行数。每个缓存行由三部分组成：<strong>有效位</strong>(valid)，<strong>标记位</strong>（tag），<strong>数据块</strong>（cache block）。</p>
<ul>
<li>有效位：该位等于 1，表示这个行数据有效。</li>
<li>标记位：唯一的标识了存储在高速缓存中的块，标识目标数据是否存在当前的缓存行中。</li>
<li>数据块：一部分内存数据的副本。</li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711171136.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711171136.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>Cache 的结构可以由元组$(S,E,B,m)$表示。不包括有效位和标记位。Cache 的大小为 $C=S \times E \times B$.</p>
<p>接下来看看 Cache 是如何工作的，当 CPU 执行数据加载指令，从内存地址 A 读取数据时，根据存储器层次原理，如果 Cache 中保存着目标数据的副本，那么就立即将数据返回给 CPU。那么 Cache 如何知道自己保存了目标数据的副本呢？</p>
<p>假设目标地址的数据长度为$m$位，这个地址被参数 $S$ 和 $B$ 分成了三个字段：</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711174416.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711174416.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>首先通过长度为$s$的<strong>组索引</strong>，确定目标数据保存在哪一个组 (Set) 中，其次通过长度为$t$的<strong>标记</strong>，确定在哪一行，需要注意的是此时有效位必须等于 1，最后根据长度为$b$的<strong>块偏移</strong>，来确定目标数据在数据块中的确切位置。</p>
<blockquote>
<p>Q：既然读取 Cache 第一步是组选择，为什么不用高位作为组索引，而使用中间的为作为组索引？
A：如果使用了高位作索引，那么一些连续的内存块就会映射到相同的高速缓存块。如图前四个块映射到第一个缓存组，第二个四个块映射到第二个组，依次类推。如果一个程序有良好的空间局部性，顺序扫描一个数组的元素，那么在任何时候，缓存中都只保存在一个块大小的数组内容。这样对缓存的使用率很低。相比而言，如果使用中间的位作为组索引，那么相邻的块总是映射到不同的组，图中的情况能够存放整个大小的数组片。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711185819.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711185819.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
</blockquote>
<h5 id="直接映射高速缓存-direct-mapped-cache">直接映射高速缓存 Direct Mapped Cache</h5>
<p>根据每个组的缓存行数 $E$ 的不同，Cache 被分为不同的类。每个组只有一行$E=1$的高速缓存被称为<strong>直接映射高速缓存</strong>(direct-mapped cache)。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711190845.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711190845.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>当一条加载指令指示 CPU 从主存地址 A 中读取一个字 w 时，会将该主存地址 A 发送到高速缓存中，则高速缓存会根据<strong>组选择</strong>，<strong>行匹配</strong>和<strong>字抽取</strong>三步来判断地址 A 是否命中。</p>
<p><strong>组选择</strong>(set selection)：根据组索引值来确定属于哪一个组，如图中索引长度为 5 位，可以检索 32 个组 ($2^5=32$)。当$s=0$时，此时组选择的结果为<code>set 0</code>，当$s=1$时，此时组选择的结果为<code>set 1</code>。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711191708.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711191708.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>行匹配 (line match)</strong>：首先看缓存行的有效位，此时有效位为 1，表示当前数据有效。然后对比缓存行的标记<code>0110</code>与地址中的标记<code>0110</code>是否相等，如果相等，则表示目标数据在当前的缓存行中（缓存命中）。如果不一致或者有效位为 0，则表示目标数据不在当前的缓存行中（缓存不命中）。如果命中，就可以进行下一步字抽取。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192435.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192435.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>字抽取 (word extraction)</strong>：根据偏移量$b$确定目标数据的确切位置，通俗来说就是从数据块的什么位置开始抽取位置。如当偏移块等于<code>100</code>时，表示目标数据起始地址位于字节 4 处。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192757.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711192757.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>下面通过一个例子来解释清除这个过程。假设我们有一个直接映射高速缓存，描述为$(S,E,B,m) = (4,1,2,4)$。换句话说，高速缓存有 4 个组，每个组 1 行，每个数据块 2 个字节，地址长度为 4 位。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194256.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194256.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>从图中可以看出，8 个内存块，但只有 4 个高速缓存组，所以会有多个块映射到同一个高速缓存组中。例如，块 0 和块 4 都会被映射到组 0。</p>
<p>下面我们来模拟当 CPU 执行一系列读的时候，高速缓存的执行情况，我们假设每次 CPU 读 1 个字节的字。</p>
<p><strong>读地址 0(0000) 的字：</strong>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711193901.gif">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711193901.gif" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>读地址 1(0001) 的字：</strong>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194838.gif">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711194838.gif" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>读地址 13(1101) 的字：</strong>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711195108.gif">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711195108.gif" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>读地址 8(1000) 的字：</strong>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200054.gif">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200054.gif" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>读地址 0(0000) 的字：</strong>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200409.gif">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220711200409.gif" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h5 id="组相联高速缓存-set-associative-cache">组相联高速缓存 Set Associative Cache</h5>
<p>由于直接映射高速缓存的组中只有一行，所以容易发生冲突不命中。组相联高速缓存 (Set associative cache) 运行有多行缓存行。但是缓存行最大不能超过 $C/B$。</p>
<p>如图一个组中包含了两行缓存行，这种我们称为 2 路相联高速缓存。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220712160513.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220712160513.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><strong>组选择</strong>：与直接映射高速缓存的组选择过程一样。</p>
<p><strong>行匹配</strong>：因为一个组有多行，所以需要遍历所有行，找到一个有效位为 1，并且标记为与地址中的标记位相匹配的一行。如果找到了，表示缓存命中。</p>
<p><strong>字抽取</strong>：根据偏移量$b$确定目标数据的确切位置，通俗来说就是从数据块的什么位置开始抽取位置。如当偏移块等于<code>100</code>时，表示目标数据起始地址位于字节 4 处。</p>
<p>如果不命中，那么就需要从主存中取出需要的数据块，但是将数据块放在哪一行缓存行呢？如果存在空行 ($valid=0$)，那就放到空行里。如果没有空行，就得选择一个非空行来替换，同时希望 CPU 不会很快引用这个被替换的行。这里介绍几个替换策略。</p>
<p>最简单的方式就是随机选择一行来替换，其他复杂的方式就是利用局部性原理，使得接下来 CPU 引用替换的行概率最小。如</p>
<h3 id="缓存一致性协议-mesi">缓存一致性协议 MESI</h3>
<h4 id="为什么需要缓存一致">为什么需要缓存一致</h4>
<p>目前主流电脑的 CPU 都是多核心的，多核心的有点就是在不能提升 CPU 主频后，通过增加核心来提升 CPU 吞吐量。每个核心都有自己的 L1 Cache 和 L2 Cache，只是共用 L3 Cache 和主内存。每个核心操作是独立的，每个核心的 Cache 就不是同步更新的，这样就会带来缓存一致性（Cache Coherence）的问题。</p>
<p>举个例子，如图：


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205291536919.gif">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205291536919.gif" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>有 2 个 CPU，主内存里有个变量<code>x=0</code>。CPU A 中有个需要将变量<code>x</code>加<code>1</code>。CPU A 就将变量<code>x</code>加载到自己的缓存中，然后将变量<code>x</code>加<code>1</code>。因为此时 CPU A 还未将缓存数据写回主内存，CPU B 再读取变量<code>x</code>时，变量<code>x</code>的值依然是<code>0</code>。</p>
<p>这里的问题就是所谓的缓存一致性问题，因为 CPU A 的缓存与 CPU B 的缓存是不一致的。</p>
<h4 id="如何解决缓存一致性问题">如何解决缓存一致性问题</h4>
<h5 id="通过在总线加-lock-锁的方式">通过在总线加 LOCK 锁的方式</h5>
<p>在锁住总线上加一个 LOCK 标识，CPU A 进行读写操作时，锁住总线，其他 CPU 此时无法进行内存读写操作，只有等解锁了才能进行操作。</p>
<p>该方式因为锁住了整个总线，所以效率低。</p>
<h5 id="缓存一致性协议-mesi-1">缓存一致性协议 MESI</h5>
<p>该方式对单个缓存行的数据进行加锁，不会影响到内存其他数据的读写。</p>
<p>在学习 MESI 协议之前，简单了解一下总线嗅探机制（Bus Snooping）。要对自己的缓存加锁，需要通知其他 CPU，多个 CPU 核心之间的数据传播问题。最常见的一种解决方案就是总线嗅探。</p>
<p>这个策略，本质上就是把所有的读写请求都通过总线广播给所有的 CPU 核心，然后让各个核心去“嗅探”这些请求，再根据本地的情况进行响应。MESI 就是基于总线嗅探机制的缓存一致性协议。</p>
<p>MESI 协议的由来是对 Cache Line 的四个不同的标记，分别是：</p>
<table>
	<thead>
			<tr>
					<th style="text-align: center"><div style="width:50px">状态</div></th>
					<th style="text-align: center"><div style="width:100px">状态</div></th>
					<th><div style="width:200px">描述</div></th>
					<th><div style="width:200px">监听任务</div></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: center">Modified</td>
					<td style="text-align: center">已修改</td>
					<td>该 Cache Line 有效，数据被修改了，和内存中的数据不一致，数据只存在于本 Cache 中</td>
					<td>Cache Line 必须时刻监听所有试图读该 Cache Line 相对于主存的操作，这种操作必须在缓存将该 Cache Line 写回主存并将状态改为 S 状态之前，被延迟执行</td>
			</tr>
			<tr>
					<td style="text-align: center">Exclusive</td>
					<td style="text-align: center">独享，互斥</td>
					<td>该 Cache Line 有效，数据和内存中的数据一直，数据只存在于本 Cache</td>
					<td>Cache Line 必须监听其他缓存读主存中该 Cache Line 的操作，一旦有这种操作，该 Cache Line 需要改为 S 状态</td>
			</tr>
			<tr>
					<td style="text-align: center">Shared</td>
					<td style="text-align: center">共享的</td>
					<td>该 Cache Line 有效，数据和内存中的数据一直，数据存在于很多个 Cache 中</td>
					<td>Cache Line 必须监听其他  Cache Line 使该 Cache Line 无效或者独享该 Cache Line 的请求，并将 Cache Line 改为 I 状态</td>
			</tr>
			<tr>
					<td style="text-align: center">Invalid</td>
					<td style="text-align: center">无效的</td>
					<td>该 Cache Line 无效</td>
					<td>无</td>
			</tr>
	</tbody>
</table>
<p>整个 MESI 的状态，可以用一个有限状态机来表示它的状态流转。需要注意的是，对于不同状态触发的事件操作，可能来自于当前 CPU 核心，也可能来自总线里其他 CPU 核心广播出来的信号。我把各个状态之间的流转用表格总结了一下：</p>
<table>
	<thead>
			<tr>
					<th style="text-align: center"><div style="width:80px">当前状态</div></th>
					<th style="text-align: center"><div style="width:80px">事件</div></th>
					<th><div style="width:300px,center">行为</div></th>
					<th style="text-align: center"><div style="width:80px">下个状态</div></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: center">M</td>
					<td style="text-align: center">Local Read</td>
					<td>从 Cache 中读，状态不变</td>
					<td style="text-align: center">M</td>
			</tr>
			<tr>
					<td style="text-align: center">M</td>
					<td style="text-align: center">Local Write</td>
					<td>修改 cache 数据，状态不变</td>
					<td style="text-align: center">M</td>
			</tr>
			<tr>
					<td style="text-align: center">M</td>
					<td style="text-align: center">Remote Read</td>
					<td>这行数据被写到内存中，使其他核能使用到最新数据，状态变为 S</td>
					<td style="text-align: center">S</td>
			</tr>
			<tr>
					<td style="text-align: center">M</td>
					<td style="text-align: center">Remote Write</td>
					<td>这行数据被写入内存中，其他核可以获取到最新数据，由于其他 CPU 修改该条数据，则本地 Cache 变为 I</td>
					<td style="text-align: center">I</td>
			</tr>
	</tbody>
</table>
<table>
	<thead>
			<tr>
					<th style="text-align: center"><div style="width:80px">当前状态</div></th>
					<th style="text-align: center"><div style="width:80px">事件</div></th>
					<th><div style="width:200px,center">行为</div></th>
					<th style="text-align: center"><div style="width:80px">下个状态</div></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: center">E</td>
					<td style="text-align: center">Local Read</td>
					<td>从 Cache 中读，状态不变</td>
					<td style="text-align: center">E</td>
			</tr>
			<tr>
					<td style="text-align: center">E</td>
					<td style="text-align: center">Local Write</td>
					<td>修改数据，状态改为 M</td>
					<td style="text-align: center">M</td>
			</tr>
			<tr>
					<td style="text-align: center">E</td>
					<td style="text-align: center">Remote Read</td>
					<td>数据和其他 CPU 共享，变为 S</td>
					<td style="text-align: center">S</td>
			</tr>
			<tr>
					<td style="text-align: center">E</td>
					<td style="text-align: center">Remote Write</td>
					<td>数据被修改，本地缓存失效，变为 I</td>
					<td style="text-align: center">I</td>
			</tr>
	</tbody>
</table>
<table>
	<thead>
			<tr>
					<th style="text-align: center"><div style="width:80px">当前状态</div></th>
					<th style="text-align: center"><div style="width:80px">事件</div></th>
					<th><div style="width:200px,text-align: center">行为</div></th>
					<th style="text-align: center"><div style="width:80px">下个状态</div></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: center">S</td>
					<td style="text-align: center">Local Read</td>
					<td>从 Cache 中读，状态不变</td>
					<td style="text-align: center">S</td>
			</tr>
			<tr>
					<td style="text-align: center">S</td>
					<td style="text-align: center">Local Write</td>
					<td>修改数据，状态改为 M，其他 CPU 的 Cache Line 状态改为 I</td>
					<td style="text-align: center">M</td>
			</tr>
			<tr>
					<td style="text-align: center">S</td>
					<td style="text-align: center">Remote Read</td>
					<td>数据和其他 CPU 共享，状态不变</td>
					<td style="text-align: center">S</td>
			</tr>
			<tr>
					<td style="text-align: center">S</td>
					<td style="text-align: center">Remote Write</td>
					<td>数据被修改，本地缓存失效，变为 I</td>
					<td style="text-align: center">I</td>
			</tr>
	</tbody>
</table>
<table>
	<thead>
			<tr>
					<th style="text-align: center"><div style="width:80px">当前状态</div></th>
					<th style="text-align: center"><div style="width:80px">事件</div></th>
					<th><div style="width:200px,center">行为</div></th>
					<th style="text-align: center"><div style="width:80px">下个状态</div></th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td style="text-align: center">I</td>
					<td style="text-align: center">Local Read</td>
					<td>1. 如果其他 CPU 没有这份数据，直接从内存中加载数据，状态变为 E；<br> 2. 如果其他 CPU 有这个数据，且 Cache Line 状态为 M，则先把 Cache Line 中的内容写回到主存。本地 Cache 再从内存中读取数据，这时两个 Cache Line 的状态都变为 S；<br>3. 如果其他 Cache Line 有这份数据，并且状态为 S 或者 E，则本地 Cache Line 从主存读取数据，并将这些 Cache Line 状态改为 S</td>
					<td style="text-align: center">E 或者 S</td>
			</tr>
			<tr>
					<td style="text-align: center">I</td>
					<td style="text-align: center">Local Write</td>
					<td>1. 先从内存中读取数据，如果其他 Cache Line 中有这份数据，且状态为 M，则现将数据更新到主存再读取，将 Cache Line 状态改为 M；<br> 2. 如果其他 Cache Line 有这份数据，且状态为 E 或者 S，则其他 Cache Line 状态改为 I</td>
					<td style="text-align: center">M</td>
			</tr>
			<tr>
					<td style="text-align: center">I</td>
					<td style="text-align: center">Remote Read</td>
					<td>数据和其他 CPU 共享，状态不变</td>
					<td style="text-align: center">S</td>
			</tr>
			<tr>
					<td style="text-align: center">I</td>
					<td style="text-align: center">Remote Write</td>
					<td>数据被修改，本地缓存失效，变为 I</td>
					<td style="text-align: center">I</td>
			</tr>
	</tbody>
</table>
<h2 id="内存">内存</h2>
<p>计算机有五大组成部分，分别是：运算器、控制器、存储器、输入设备和输出设备。而内存就是其中的存储器。我们的数据和指令都需要先放到内存中，然后再被 CPU 执行。</p>
<p>操作系统中程序并不能直接访问物理内存，我们的内存需要被分成固定大小的页（Page），然后再通过<strong>虚拟内存地址（Virtual Address）到物理内存地址（Physical Address）的地址转换（Address Translation）</strong>，才能到达实际存放数据的物理内存位置。而我们的程序看到的内存地址，都是虚拟内存地址。那么如何进行转换的呢？</p>
<h3 id="简单页表">简单页表</h3>
<p>最简单的方式，就是建立一张虚拟内存到物理内存的映射表，在计算机里叫做页表（Page Table）。页表这个地址转换的办法，会把一个内存地址分成页号（Directory）和偏移量（Offset）两个部分，是不是似曾相识，因为在前面的高速缓存里，缓存的结构也是这样的。</p>
<p>以一个 32 位地址举例，高 20 位是虚拟页号，可以从虚拟页表中找到物理页号的信息，低 12 位是偏移量，可以准确获得物理地址。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161640968.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161640968.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>总结一下，对于一个内存地址转换，其实就是这样三个步骤：</p>
<ul>
<li>把虚拟内存地址，切分成页号和偏移量的组合；</li>
<li>从页表里面，查询出虚拟页号，对应的物理页号；</li>
<li>直接拿物理页号，加上前面的偏移量，就得到了物理内存地址。</li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161645714.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202207161645714.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>但是这样的页表有个问题，它需要记录$2^{20}$个物理页表，这个存储关系，就好比一个 $2^{20}$大小的数组。一个页号是完整的 32 位的 4 字节（Byte），这样一个页表就需要 4MB 的空间。并且每个进程都会有这样一个页表，现代电脑正常都有成百上千个进程，如果用这样的页表肯定行不通的。</p>
<h3 id="多级页表">多级页表</h3>
<p>所以，<strong>在一个实际的程序进程里面，虚拟内存占用的地址空间，通常是两段连续的空间。而不是完全散落的随机的内存地址</strong>。而多级页表，就特别适合这样的内存地址分布。</p>
<p><a href="https://zhuanlan.zhihu.com/p/357648933">谈一谈内存管理，虚拟内存，多级页表 - 知乎</a></p>
<h3 id="tlb">TLB</h3>
<h3 id="内存保护---可执行空间保护">内存保护 - 可执行空间保护</h3>
<h3 id="内存保护---地址空间布局随机化">内存保护 - 地址空间布局随机化</h3>
<p>Address Space Layout Randomization</p>
<h2 id="总线计算机内部的高速公路">总线：计算机内部的高速公路</h2>
<p>计算机由控制器、运算器、存储器、输入设备以及输出设备五大部分组成。CPU 所代表的控制器和运算器，要和存储器，也就是我们的主内存，以及输入和输出设备进行通信。那么计算机是用什么样的方式来完成，CPU 和内存、以及外部输入输出设备的通信呢？答案就是通过总线来通信。</p>
<p>计算机里有不同的硬件设备，如果设备与设备之间都单独连接，那么就需要 N*N 的连线。那么怎么降低复杂度呢？与其让各个设备之间互相单独通信，不如我们去设计一个公用的线路。CPU 想要和什么设备通信，通信的指令是什么，对应的数据是什么，都发送到这个线路上；设备要向 CPU 发送什么信息呢，也发送到这个线路上。这个线路就好像一个高速公路，各个设备和其他设备之间，不需要单独建公路，只建一条小路通向这条高速公路就好了。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510203.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510203.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510711.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081510711.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h3 id="三种线路和多总线架构">三种线路和多总线架构</h3>
<p>首先，CPU 和内存以及高速缓存通信的总线，这里面通常有两种总线。这种方式，我们称之为双独立总线（Dual Independent Bus，缩写为 DIB）。CPU 里，有一个快速的本地总线（Local Bus），以及一个速度相对较慢的前端总线（Front-side Bus）。</p>
<p>现代的 CPU 里，通常有专门的高速缓存芯片。这里的高速本地总线，就是用来和高速缓存通信的。而前端总线，则是用来和主内存以及输入输出设备通信的。有时候，我们会把本地总线也叫作后端总线（Back-sideBus），和前面的前端总线对应起来。</p>
<p>除了前端总线呢，我们常常还会听到 PCI 总线、I/O 总线或者系统总线（System Bus）。看到这么多总线的名字，你是不是已经有点晕了。这些名词确实容易混为一谈。其实各种总线的命名一直都很混乱，我们不如直接来看一看 CPU 的硬件架构图。对照图来看，一切问题就都清楚了。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081513938.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081513938.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>CPU 里面的北桥芯片，把我们上面说的前端总线，一分为二，变成了三个总线。我们的前端总线，其实就是系统总线。CPU 里面的内存接口，直接和系统总线通信，然后系统总线再接入一个 I/O 桥接器（I/OBridge）。这个 I/O 桥接器，一边接入了我们的内存总线，使得我们的 CPU 和内存通信；另一边呢，又接入了一个 I/O 总线，用来连接 I/O 设备。</p>
<p>事实上，真实的计算机里，这个总线层面拆分得更细。根据不同的设备，还会分成独立的 PCI 总线、ISA 总线等等。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081516341.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202205081516341.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>在物理层面，其实我们完全可以把总线看作一组“电线”。不过呢，这些电线之间也是有分工的，我们通常有三类线路。</p>
<ol>
<li>数据线（Data Bus），用来传输实际的数据信息，也就是实际上了公交车的“人”。</li>
<li>地址线（Address Bus），用来确定到底把数据传输到哪里去，是内存的某个位置，还是某一个 I/O 设备。这个其实就相当于拿了个纸条，写下了上面的人要下车的站点。</li>
<li>控制线（ControlBus），用来控制对于总线的访问。虽然我们把总线比喻成了一辆公交车。那么有人想要做公交车的时候，需要告诉公交车司机，这个就是我们的控制信号。</li>
</ol>
<p>尽管总线减少了设备之间的耦合，也降低了系统设计的复杂度，但同时也带来了一个新问题，那就是<strong>总线不能同时给多个设备提供通信功能</strong>。</p>
<p>我们的总线是很多个设备公用的，那多个设备都想要用总线，我们就需要有一个机制，去决定这种情况下，到底把总线给哪一个设备用。这个机制，就叫作<strong>总线裁决</strong>（Bus Arbitraction）</p>
<h2 id="硬盘">硬盘</h2>
<h2 id="dma">DMA</h2>
<p>过去几年，计算机产业一直在为提升 I/O 设备的速度而努力，从机械硬盘 HDD 到固态硬盘 SSD，从 SATA 协议到 PCIE 协议，虽然速度都几十上百倍的增加，但是仍然不够快。因为相比于 CPU 基本都是 2GHz 的频率（每秒会有 20 亿次的操作），SSD 硬盘的 IOPS 的 2 万次操作就显得微不足道。</p>
<p>如果我们对于 I/O 的操作，都是由 CPU 发出对应的指令，然后等待 I/O 设备完成操作之后返回，那 CPU 有大量的时间其实都是在等待 I/O 设备完成操作。特别是当传输的数据量比较大的时候，比如进行大文件复制，如果所有数据都要经过 CPU，实在是有点儿太浪费时间了。</p>
<p>因此，计算机工程师们，就发明了<strong>DMA 技术</strong>，也就是<strong>直接内存访问（Direct Memory Access）技术</strong>，来减少 CPU 等待的时间。</p>
<h3 id="什么是-dma">什么是 DMA</h3>
<p>本质上，DMA 技术就是我们在主板上放一块<strong>独立的芯片</strong>。在进行内存和 I/O 设备的数据传输的时候，我们不再通过 CPU 来控制数据传输，而直接通过 DMA 控制器（DMA Controller，简称 DMAC）。这块芯片，我们可以认为它其实就是一个<strong>协处理器</strong>（Co-Processor）。</p>
<p>DMAC 最有价值的地方体现在，当我们要传输的数据特别大、速度特别快，或者传输的数据特别小、速度特别慢的时候。</p>
<p>比如说，我们用千兆网卡或者硬盘传输大量数据的时候，如果都用 CPU 来搬运的话，肯定忙不过来，所以可以选择 DMAC。而当数据传输很慢的时候，DMAC 可以等数据到齐了，再向 CPU 发起中断，让 CPU 去处理，而不是让 CPU 在那里忙等待。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202208152252016.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/202208152252016.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<ol>
<li>首先，CPU 还是作为一个主设备，向 DMAC 设备发起请求。这个请求，其实就是在 DMAC 里面修改配置寄存器。</li>
<li>CPU 修改 DMAC 的配置的时候，会告诉 DMAC 这样几个信息：
<ul>
<li>源地址的初始值：数据要从哪里传输过来。如果我们要从内存里面写入数据到硬盘上，那么就是要读取的数据在内存里面的地址</li>
<li>传输时候的地址增减方式：数据是从大的地址向小的地址传输，还是从小的地址往大的地址传输</li>
<li>传输的数据长度：也就是我们一共要传输多少数据</li>
</ul>
</li>
<li>设置完这些信息之后，DMAC 就会变成一个空闲的状态（Idle）。</li>
<li>如果我们要从硬盘上往内存里面加载数据，这个时候，硬盘就会向 DMAC 发起一个数据传输请求。这个请求并不是通过总线，而是通过一个额外的连线。</li>
<li>然后，我们的 DMAC 需要再通过一个额外的连线响应这个申请。</li>
<li>DMAC 这个芯片，就向硬盘的接口发起要总线读的传输请求。数据就从硬盘里面，读到了 DMAC 的控制器里面。</li>
<li>DMAC 再向我们的内存发起总线写的数据传输请求，把数据写入到内存里面。</li>
<li>DMAC 会反复进行上面第 6、7 步的操作，直到 DMAC 的寄存器里面设置的数据长度传输完成。</li>
<li>数据传输完成之后，DMAC 重新回到第 3 步的空闲状态。</li>
</ol>
<p>所以，整个数据传输的过程中，我们不是通过 CPU 来搬运数据，而是由 DMAC 这个芯片来搬运数据。但是 CPU 在这个过程中也是必不可少的。因为传输什么数据，从哪里传输到哪里，其实还是由 CPU 来设置的。这也是为什么，DMAC 被叫作 <strong>协处理器</strong>。</p>
<h2 id="参考资料">参考资料</h2>
<p><a href="https://www.bilibili.com/video/BV1cJ411K7HW?spm_id_from=333.999.0.0">【硬件科普】电脑主板右下角的散热片下面究竟隐藏着什么？详解主板南桥芯片组的功能和作用_哔哩哔哩_bilibili</a></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
