<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://zhanlutuzi.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://zhanlutuzi.github.io/" rel="alternate" type="text/html" hreflang="zh-CN" /><updated>2026-04-26T10:56:25+08:00</updated><id>https://zhanlutuzi.github.io/feed.xml</id><title type="html">湛泸兔子</title><subtitle>Stand on the shoulder of giants</subtitle><author><name>zhanlutuzi</name></author><entry><title type="html">我是如何从一串问题，搭起大模型知识结构的</title><link href="https://zhanlutuzi.github.io/2026/04/25/llm-knowledge-structure/" rel="alternate" type="text/html" title="我是如何从一串问题，搭起大模型知识结构的" /><published>2026-04-25T23:20:00+08:00</published><updated>2026-04-25T23:20:00+08:00</updated><id>https://zhanlutuzi.github.io/2026/04/25/llm-knowledge-structure</id><content type="html" xml:base="https://zhanlutuzi.github.io/2026/04/25/llm-knowledge-structure/"><![CDATA[<p>我最开始问 AI 的问题很普通：</p>

<blockquote>
  <p>大模型的发展历程是什么？</p>
</blockquote>

<p>我以为自己需要的是一张时间表。哪一年出现 Transformer，哪一年出现 GPT-3，哪一年 ChatGPT 火了，哪一年 GPT-4 开始支持多模态，哪一年 DeepSeek 又被大家讨论。</p>

<p>但问着问着我发现，时间线只能告诉我“发生了什么”，不能告诉我“我为什么不懂”。</p>

<p>我真正卡住的地方不是某个模型在哪一年发布，也不是某个模型有多少参数。我真正卡住的是：这些名字背后到底有什么关系？为什么它们都叫大模型？为什么大家反复提 Transformer？MoE 是不是一种新架构？FFN 为什么和模型的“记忆”有关？CLIP 和 SAM 又为什么会出现在多模态大模型的讨论里？</p>

<p>也就是说，我一开始以为自己在问大模型历史，后来才发现自己其实是在问：</p>

<blockquote>
  <p>我该如何建立一套能放下这些概念的知识结构？</p>
</blockquote>

<p>这篇文章就是我给自己的答案。它不是论文综述，也不是百科词条，而是一份学习地图：以后我忘了某个概念，可以回到这里，先找到它在整张图里的位置。</p>

<h2 id="时间线只是入口">时间线只是入口</h2>

<p>大模型的发展很容易写成流水账：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>深度学习兴起
  -&gt; Transformer 出现
  -&gt; BERT / GPT 开启预训练时代
  -&gt; GPT-3 展示规模化能力
  -&gt; ChatGPT 让对话式 AI 出圈
  -&gt; GPT-4 / GPT-4o 推动多模态
  -&gt; DeepSeek 等模型让效率、成本和推理能力成为焦点
</code></pre></div></div>

<p>这些节点当然重要。但如果只记年份，我还是不知道大模型为什么会变强。</p>

<p>后来我把时间线压缩成几次关键变化：</p>

<table>
  <thead>
    <tr>
      <th>变化</th>
      <th>我真正要记住的东西</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>从手工规则到数据学习</td>
      <td>能力不是一条条规则写进去的，而是模型从大量数据里学出来的</td>
    </tr>
    <tr>
      <td>Transformer 出现</td>
      <td>大规模语言模型有了一个非常重要的架构底座</td>
    </tr>
    <tr>
      <td>预训练成为主流</td>
      <td>模型先学通用语言能力，再适配具体任务</td>
    </tr>
    <tr>
      <td>规模化能力显现</td>
      <td>模型开始从专用工具变成通用接口</td>
    </tr>
    <tr>
      <td>ChatGPT 出圈</td>
      <td>对话界面改变了普通人调用 AI 的方式</td>
    </tr>
    <tr>
      <td>多模态发展</td>
      <td>模型不再只处理文字，而开始处理图像、音频等信息</td>
    </tr>
    <tr>
      <td>架构效率竞争</td>
      <td>继续变大之外，成本、推理、长上下文和部署效率也变得关键</td>
    </tr>
  </tbody>
</table>

<p>所以，大模型发展史对我来说不是“模型排行榜”，而是一条能力扩展路线：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>数据学习
  -&gt; 可扩展架构
  -&gt; 预训练
  -&gt; 规模化
  -&gt; 对话交互
  -&gt; 多模态
  -&gt; 推理与效率
</code></pre></div></div>

<h2 id="transformer-不是-gpt而是一棵家族树">Transformer 不是 GPT，而是一棵家族树</h2>

<p>我一开始很容易把 Transformer、GPT、大模型混在一起，仿佛它们说的是同一件事。</p>

<p>后来我才意识到，Transformer 更像一个底座。在这个底座上，长出了几条不同路线。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Transformer
  |
  |-- Encoder-only
  |     |-- BERT
  |     |-- 偏理解：分类、检索、抽取、语义匹配
  |
  |-- Decoder-only
  |     |-- GPT
  |     |-- LLaMA
  |     |-- DeepSeek
  |     |-- 偏生成：对话、写作、代码、推理
  |
  |-- Encoder-Decoder
        |-- T5
        |-- BART
        |-- 一些翻译、摘要、OCR、跨模态转换任务
</code></pre></div></div>

<p>这张家谱树对我很重要。</p>

<p>BERT 和 GPT 都是 Transformer 路线，但它们不是同一种用法。BERT 更像理解器，擅长看完整上下文后做判断。GPT 更像生成器，按顺序预测下一个 Token。T5、BART 这类 Encoder-Decoder 模型，则更像把一种输入转换成另一种输出。</p>

<p>今天很多对话大模型走的是 Decoder-only 路线，因为对话、写作、代码生成，本质上都可以被组织成“根据前文继续生成下一个 Token”的任务。</p>

<p>这也解释了一个常见误会：</p>

<blockquote>
  <p>Transformer 不是 GPT。GPT 是 Transformer 家族里 Decoder-only 这一支的代表。</p>
</blockquote>

<h2 id="一句话进入模型后数据怎么流">一句话进入模型后，数据怎么流</h2>

<p>知道模型家谱还不够。我还想知道：我输入一句话以后，模型内部到底发生了什么？</p>

<p>一个简化的数据流是这样的：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>输入文字
  -&gt; Tokenizer
  -&gt; Token IDs
  -&gt; Embedding
  -&gt; 多层 Transformer Block
      -&gt; RMSNorm / LayerNorm
      -&gt; Attention
      -&gt; 残差连接
      -&gt; RMSNorm / LayerNorm
      -&gt; FFN / MoE
      -&gt; 残差连接
  -&gt; Final Norm
  -&gt; LM Head
  -&gt; Softmax
  -&gt; 下一个 Token
</code></pre></div></div>

<p>这里每个词都有位置。</p>

<p>Tokenizer 负责把文字切成 Token。模型不直接处理自然语言，而是处理 Token 对应的编号。现代分词常见思路之一是 BPE：从更小的字节或字符片段开始，逐步合并高频片段。这样做的好处是，即使遇到生僻词，也可以拆成更小单位，不至于完全不认识。</p>

<p>Embedding 负责把 Token ID 变成向量。编号本身没有语义距离，向量才是后续计算真正处理的对象。</p>

<p>Attention 负责让 Token 之间互相看见。比如一句话里“它”指代什么，需要看上下文；Attention 就是在处理这种上下文关系。</p>

<p>残差连接负责把原始输入加回来。它像是保留底稿，再叠加每一层的新处理结果，避免深层网络里信息和梯度传不下去。</p>

<p>RMSNorm 或 LayerNorm 负责稳定数值。RMSNorm 可以理解成“稳压器”或“音量调节器”：它不负责产生知识，但能让向量的数值幅度保持在比较稳定的范围里，避免层数深了以后数值越来越大或越来越小。</p>

<p>LM Head 负责把模型内部向量映射回词表。Softmax 再把分数变成概率，模型据此选择或采样下一个 Token。</p>

<p>所以，语言模型生成一段话，本质上是在循环做一件事：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>根据已有上下文
  -&gt; 预测下一个 Token
  -&gt; 把新 Token 拼回上下文
  -&gt; 再预测下一个 Token
</code></pre></div></div>

<h2 id="ffn-让我理解模型的知识不是数据库">FFN 让我理解：模型的知识不是数据库</h2>

<p>在 Transformer Block 里，Attention 很容易吸引注意力，因为它名字响亮，也确实重要。</p>

<p>但我后来发现，FFN 同样关键。</p>

<p>一个典型 FFN 可以粗略理解成：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>输入向量
  -&gt; 升维
  -&gt; 激活函数
  -&gt; 降维
  -&gt; 输出向量
</code></pre></div></div>

<p>Attention 更像信息调度：当前 Token 应该参考哪些上下文？哪些位置的信息更重要？</p>

<p>FFN 更像深度加工：拿到上下文信息后，当前这个向量应该触发什么模式？哪些语义、事实、风格、输出倾向应该被激活？</p>

<p>有研究把 Transformer 的 FFN 层解释成一种 Key-Value Memory。这个说法对我很有帮助，因为它让我理解了模型“记住知识”的方式。</p>

<p>模型不是像数据库一样存：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>key: 法国首都
value: 巴黎
</code></pre></div></div>

<p>它更像是在训练过程中，把大量文本模式压缩进参数里。生成时，当前上下文会激活某些参数模式，从而影响下一个 Token 的概率。</p>

<p>所以“模型记住了知识”这句话要谨慎理解。它不是查表，而是在高维参数空间里做概率计算。这也解释了为什么模型既能答对很多事实，也会产生看起来合理但实际错误的幻觉。</p>

<h2 id="moe-让我理解模型大不等于每次都全部运行">MoE 让我理解：模型大，不等于每次都全部运行</h2>

<p>理解 FFN 之后，MoE 就容易理解很多。</p>

<p>MoE 的英文是 Mixture of Experts，中文常叫混合专家模型。但这个翻译本身并不能让我理解它。</p>

<p>真正有用的理解是：</p>

<blockquote>
  <p>MoE 通常不是推翻 Transformer，而是改造 Transformer 里的 FFN 部分。</p>
</blockquote>

<p>普通 FFN 是每个 Token 都走同一套前馈网络：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Token 向量
  -&gt; 一个 FFN
  -&gt; 输出
</code></pre></div></div>

<p>MoE 则是把 FFN 拆成很多专家：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Token 向量
  -&gt; Router
  -&gt; 选择少数专家 FFN
  -&gt; 合并专家输出
</code></pre></div></div>

<p>这里有两个概念一定要分清：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>总参数：模型一共有多少参数
激活参数：处理一个 Token 时实际参与计算的参数
</code></pre></div></div>

<p>MoE 的核心价值在于，它可以让模型拥有很大的总容量，但每次只激活一部分参数。这样模型不是“免费变强”，而是在容量和计算成本之间做了新的权衡。</p>

<p>当然，MoE 也会带来新问题：Router 怎么训练？专家负载是否均衡？不同设备之间通信成本如何控制？专家是否真的学出了不同能力？这些都不是“多放几个专家”就能自动解决的。</p>

<h2 id="deepseek-v3把这些概念串起来">DeepSeek-V3：把这些概念串起来</h2>

<p>DeepSeek-V3 是一个很适合用来串联这些概念的案例。</p>

<p>根据 DeepSeek-V3 Technical Report，它是一个基于 Transformer 的 MoE 语言模型，总参数约 671B，每个 Token 激活约 37B 参数，训练数据约 14.8T tokens，支持 128K 上下文。它的几个关键词包括 Decoder-only、DeepSeekMoE、MLA、RMSNorm、RoPE、FP8 混合精度、DualPipe 等。</p>

<p>这些词如果单独看，会很散。但放进数据流里就清楚了：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>输入文字
  -&gt; Tokenizer
  -&gt; Embedding
  -&gt; 多层 Decoder-only Transformer Block
      -&gt; RMSNorm：稳定数值
      -&gt; MLA：处理注意力，同时压缩 KV Cache 成本
      -&gt; 残差连接：保留原始信息
      -&gt; RMSNorm
      -&gt; DeepSeekMoE：Router 选择专家，稀疏激活 FFN
      -&gt; 残差连接
  -&gt; Final RMSNorm
  -&gt; LM Head
  -&gt; Softmax
  -&gt; 下一个 Token
</code></pre></div></div>

<p>MLA 和 MoE 不是一回事。</p>

<p>MLA 是注意力机制上的优化，核心目标之一是减少长上下文推理时 KV Cache 的压力。</p>

<p>MoE 是 FFN 路径上的稀疏化改造，核心目标是扩大模型总容量，同时控制每个 Token 实际激活的计算量。</p>

<p>这也是我理解 DeepSeek-V3 的关键：它不是“一个神秘的新模型”，而是把现代大模型里的几个重要方向组合在了一起。</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Decoder-only Transformer
  + MLA 注意力优化
  + MoE 稀疏专家
  + 低精度训练和并行工程
  + 长上下文支持
</code></pre></div></div>

<h2 id="注意力机制也有自己的演化">注意力机制也有自己的演化</h2>

<p>对话记录里还提到了一条很重要的线：注意力机制本身也在演化。</p>

<p>最原始、最经典的是 MHA，也就是 Multi-Head Attention。它让模型用多个注意力头从不同角度看上下文。</p>

<p>后来出现了 MQA、GQA 等做法，目标之一是减少 Key / Value 的缓存和计算成本。再后来，DeepSeek-V3 这类模型把 MLA 推到很显眼的位置，通过潜在向量压缩 Key / Value 表示，进一步降低长上下文推理压力。</p>

<p>还有一些路线会尝试稀疏注意力、线性注意力、滑动窗口注意力等，目标都是面对同一个问题：</p>

<blockquote>
  <p>上下文越长，注意力越贵，KV Cache 越重。</p>
</blockquote>

<p>我现在不会把这些名字都背下来，而是把它们放到同一个问题下面：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>如何让模型在更长上下文里，仍然算得动、存得下、跑得快？
</code></pre></div></div>

<p>这比单独记 MHA、GQA、MLA、Sparse Attention 更有用。</p>

<h2 id="多模态不是给语言模型加个图片输入框">多模态不是“给语言模型加个图片输入框”</h2>

<p>我一开始以为，多模态大模型就是“模型会看图”。</p>

<p>后来发现这句话太粗糙。</p>

<p>“看图”至少可以拆成两个问题：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>这张图表达了什么语义？
图里的东西具体在哪里？
</code></pre></div></div>

<p>CLIP 更接近解决第一个问题。</p>

<p>CLIP 的核心是图文语义对齐。它把图像和文本放到相近的语义空间里，让模型能判断一张图和一句话是否匹配。它很适合理解“这张图大概是什么”“这段文字和这张图是否对应”。</p>

<p>SAM 更接近解决第二个问题。</p>

<p>SAM 的核心是图像分割。它擅长把图里的目标区域分出来，回答“这个东西在哪里”。但它本身不一定告诉你这个区域在语义上是什么。</p>

<p>所以我现在这样记：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CLIP：更像知道“这是什么”
SAM：更像知道“在哪里”
</code></pre></div></div>

<p>当然，这只是帮助理解的简化说法。严格讲，CLIP 做的是图文对齐，SAM 做的是分割提示下的视觉区域提取。</p>

<p>多模态大模型要做的事情，是把这些视觉能力和语言模型接起来。</p>

<p>一种常见路线是拼接式：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>图像
  -&gt; 视觉编码器
  -&gt; 投影器
  -&gt; 语言模型
  -&gt; 文本回答
</code></pre></div></div>

<p>视觉编码器负责把图片变成视觉特征。投影器负责把视觉特征转换到语言模型能接收的表示空间。语言模型再基于这些视觉信息进行回答、推理或生成。</p>

<p>另一种路线更强调原生多模态，把文本、图像、音频甚至视频统一成更一致的表示，让不同模态更深地进入同一套推理流程。GPT-4o 这类模型让我意识到，多模态不是简单多一个输入框，而是模型感知和表达世界的边界被扩展了。</p>

<p>对话里还提到 DeepSeek-OCR 这类思路：把文本渲染成图像，通过视觉编码进行信息压缩，再交给解码器处理。这让我看到一个有意思的方向：多模态不只是“看图片”，也可能成为信息压缩和跨模态转换的新工具。</p>

<h2 id="训练和对齐模型不是预训练完就能聊天">训练和对齐：模型不是预训练完就能聊天</h2>

<p>我一开始也容易忽略训练流程。</p>

<p>大模型不是预训练完就自然变成 ChatGPT。一个粗略流程是：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>预训练
  -&gt; SFT 监督微调
  -&gt; 对齐训练
  -&gt; 安全、风格和工具使用能力调整
</code></pre></div></div>

<p>预训练让模型学会语言和世界中的大量统计模式。SFT 让模型学会按照指令回答。对齐训练则让模型更接近人类偏好，比如更有帮助、更安全、更符合对话习惯。</p>

<p>对话里出现了 PPO、DPO、GRPO 这些词，我现在先把它们放在地图上：</p>

<p>PPO 是一种强化学习式的对齐方法。典型 RLHF 流程里会出现策略模型、参考模型、奖励模型、价值模型等角色。它比较复杂，但曾经是对齐训练里非常重要的路线。</p>

<p>DPO 更直接。它利用“哪个回答更好”的偏好数据，让模型直接学习偏好差异，不一定显式训练奖励模型。</p>

<p>GRPO 则常被放在推理、数学、代码等任务讨论里。它的直觉是让同一组候选答案内部比较，用组内相对表现来提供训练信号。</p>

<p>这一块我还没有完全吃透，但我现在至少知道它们属于哪一层：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>模型架构：Transformer / MoE / Attention
训练阶段：预训练 / SFT / PPO / DPO / GRPO
产品表现：对话质量 / 安全性 / 推理能力 / 工具使用
</code></pre></div></div>

<p>不能把它们混在一起。</p>

<h2 id="工程落地模型文件推理系统和分词器也在地图里">工程落地：模型文件、推理系统和分词器也在地图里</h2>

<p>如果只看模型架构，还是不完整。</p>

<p>真正把大模型跑起来，还会遇到工程工具链。</p>

<p>DeepSpeed 主要解决大模型分布式训练的问题，典型关键词是 ZeRO 等内存优化技术。</p>

<p>vLLM 主要解决高并发推理服务的问题，典型关键词是 PagedAttention。</p>

<p>Safetensors 是 Hugging Face 生态里常见的安全、快速的模型权重存储格式。</p>

<p>MLX 是 Apple Silicon 上本地推理和机器学习计算相关的工具链。</p>

<p>Hugging Face 模型仓库里常见几个文件：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>config.json
  -&gt; 模型结构配置，比如层数、隐藏维度、注意力头数

*.safetensors
  -&gt; 模型权重，也就是训练出来的参数

tokenizer.json
  -&gt; 分词器配置，包括词表和切分规则

README.md
  -&gt; 模型卡，说明模型用途、训练信息、限制和许可证
</code></pre></div></div>

<p>这让我意识到，“一个模型”不只是一个抽象名字。真正落地时，它会变成配置、权重、分词器、推理框架、部署服务、显存管理和 API 成本。</p>

<h2 id="比较不同模型时我现在会问什么">比较不同模型时，我现在会问什么</h2>

<p>对话里还问过 MiniMax、GLM、DeepSeek 这些模型架构有没有区别。</p>

<p>这类问题很容易变成参数表，但参数表很快会过期。具体版本、价格、上下文长度、指标排名都需要查最新官方资料，不能靠聊天记录里的二手信息直接写死。</p>

<p>但比较框架可以保留下来。</p>

<p>以后我看到一个新模型，会先问：</p>

<table>
  <thead>
    <tr>
      <th>维度</th>
      <th>我应该问什么</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>基础架构</td>
      <td>是 Encoder-only、Decoder-only，还是 Encoder-Decoder？</td>
    </tr>
    <tr>
      <td>参数结构</td>
      <td>是 Dense 还是 MoE？总参数和激活参数分别是多少？</td>
    </tr>
    <tr>
      <td>注意力机制</td>
      <td>用 MHA、GQA、MLA、稀疏注意力，还是线性注意力？</td>
    </tr>
    <tr>
      <td>上下文能力</td>
      <td>标称上下文多长？长上下文真实效果如何？</td>
    </tr>
    <tr>
      <td>多模态能力</td>
      <td>支持哪些输入输出？是拼接式还是更原生的融合？</td>
    </tr>
    <tr>
      <td>训练方式</td>
      <td>预训练、SFT、偏好对齐、推理强化分别怎么做？</td>
    </tr>
    <tr>
      <td>工程效率</td>
      <td>推理成本、延迟、吞吐、显存占用如何？</td>
    </tr>
    <tr>
      <td>开放程度</td>
      <td>是否开源？是否有模型卡、权重、推理代码？</td>
    </tr>
  </tbody>
</table>

<p>这比单纯问“哪个模型更强”有用。</p>

<p>因为模型强不强，往往取决于场景。写作、代码、数学、长文档、多模态、本地部署、低成本 API，关注点都不一样。</p>

<h2 id="最后得到的知识地图">最后得到的知识地图</h2>

<p>整理到最后，我真正需要的是这样一张地图：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>大模型知识结构
  |
  |-- 发展脉络
  |     |-- 数据学习
  |     |-- Transformer
  |     |-- 预训练
  |     |-- 规模化
  |     |-- 对话交互
  |     |-- 多模态
  |     |-- 推理与效率
  |
  |-- Transformer 家族
  |     |-- Encoder-only：BERT
  |     |-- Decoder-only：GPT / LLaMA / DeepSeek
  |     |-- Encoder-Decoder：T5 / BART / OCR 和转换类任务
  |
  |-- 模型内部数据流
  |     |-- Tokenizer / BPE
  |     |-- Embedding
  |     |-- Attention
  |     |-- FFN
  |     |-- RMSNorm / LayerNorm
  |     |-- Residual
  |     |-- LM Head / Softmax
  |
  |-- 架构优化
  |     |-- MHA / GQA / MLA
  |     |-- KV Cache
  |     |-- MoE / Router / Experts
  |     |-- RoPE
  |     |-- Dense vs Sparse
  |
  |-- 现代模型案例
  |     |-- DeepSeek-V3
  |     |-- DeepSeekMoE
  |     |-- MLA
  |     |-- FP8 / DualPipe
  |
  |-- 视觉和多模态
  |     |-- CLIP：图文语义对齐
  |     |-- SAM：图像分割
  |     |-- 视觉编码器
  |     |-- 投影器
  |     |-- 多模态 LLM
  |     |-- DeepSeek-OCR 这类跨模态压缩思路
  |
  |-- 训练和对齐
  |     |-- 预训练
  |     |-- SFT
  |     |-- PPO
  |     |-- DPO
  |     |-- GRPO
  |
  |-- 工程落地
        |-- DeepSpeed
        |-- vLLM
        |-- Safetensors
        |-- MLX
        |-- Hugging Face 仓库结构
        |-- Tokenizer / config / weights
</code></pre></div></div>

<p>这张图不保证覆盖所有细节，但它解决了我最初的问题。</p>

<p>我以后再看到一个名词，可以先问：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>它属于发展史、架构、数据流、训练、多模态，还是工程？
它在模型里哪个位置？
它解决什么问题？
它带来什么代价？
它和我已经知道的哪个概念相邻？
</code></pre></div></div>

<p>这比背定义更重要。</p>

<h2 id="写给未来的自己">写给未来的自己</h2>

<p>我写这篇文章，首先是写给自己看的。</p>

<p>因为我知道自己一定会忘。过一段时间，我可能又会忘记 MoE 和 FFN 的关系，忘记 RMSNorm 放在哪里，忘记 CLIP 和 SAM 的区别，忘记 DeepSeek-V3 的 MLA 到底优化什么，也忘记 PPO、DPO、GRPO 分别属于训练流程里的哪一段。</p>

<p>但没关系。</p>

<p>我不需要一次性记住所有名词。我只需要留下一条能回来的路。</p>

<p>这条路不是从定义开始，而是从问题开始：</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>我为什么会困惑？
这个概念在结构里的位置是什么？
它前后连接了哪些东西？
如果我忘了它，应该回到哪张图？
</code></pre></div></div>

<p>这也是我现在对学习技术概念最大的体会：</p>

<blockquote>
  <p>知识不是靠一次性记住所有定义建立起来的，而是靠不断给概念找到位置建立起来的。</p>
</blockquote>

<p>这篇文章就是我给自己画下的第一张大模型地图。</p>

<h2 id="参考来源">参考来源</h2>

<ul>
  <li>Transformer: https://arxiv.org/abs/1706.03762</li>
  <li>BERT: https://arxiv.org/abs/1810.04805</li>
  <li>GPT-2: https://openai.com/index/better-language-models/</li>
  <li>GPT-3: https://openai.com/index/language-models-are-few-shot-learners/</li>
  <li>GPT-4: https://openai.com/index/gpt-4-research/</li>
  <li>GPT-4o: https://openai.com/index/hello-gpt-4o/</li>
  <li>Transformer Feed-Forward Layers Are Key-Value Memories: https://aclanthology.org/2021.emnlp-main.446/</li>
  <li>RMSNorm: https://arxiv.org/abs/1910.07467</li>
  <li>DeepSeekMoE: https://arxiv.org/abs/2401.06066</li>
  <li>DeepSeek-V3 Technical Report: https://arxiv.org/abs/2412.19437</li>
  <li>CLIP: https://arxiv.org/abs/2103.00020</li>
  <li>Segment Anything: https://arxiv.org/abs/2304.02643</li>
</ul>]]></content><author><name>zhanlutuzi</name></author><category term="人工智能" /><category term="笔记" /><category term="大模型" /><category term="人工智能" /><category term="Transformer" /><category term="多模态" /><category term="DeepSeek" /><category term="学习笔记" /><category term="知识结构" /><summary type="html"><![CDATA[我最开始问 AI 的问题很普通：]]></summary></entry><entry><title type="html">VSCode Leaflet 自动补全问题的解决</title><link href="https://zhanlutuzi.github.io/2023/03/02/vscode-leaflet-auto-complete/" rel="alternate" type="text/html" title="VSCode Leaflet 自动补全问题的解决" /><published>2023-03-02T00:00:00+08:00</published><updated>2023-03-02T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2023/03/02/vscode-leaflet-auto-complete</id><content type="html" xml:base="https://zhanlutuzi.github.io/2023/03/02/vscode-leaflet-auto-complete/"><![CDATA[<p>使用npm的命令</p>
<p><code>npm install --save @types/leaflet</code></p>
<p>这行代码会自动下载Leaflet的TypeScript定义，并自动添加到<code>packages.json</code>文件中的dependency项。</p>
<p>安装之后重新启动VScode可以发现自动补全功能又出现了</p>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="技巧" /><summary type="html"><![CDATA[使用npm的命令 npm install --save @types/leaflet 这行代码会自动下载Leaflet的TypeScript定义，并自动添加到packages.json文件中的dependency项。 安装之后重新启动VScode可以发现自动补全功能又出现了]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/2048px-Visual_Studio_Code_1.35_icon.svg.png" /><media:content medium="image" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/2048px-Visual_Studio_Code_1.35_icon.svg.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Python Convert CSV to Shapefile</title><link href="https://zhanlutuzi.github.io/2022/09/28/python-convert-csv-to-shapefile/" rel="alternate" type="text/html" title="Python Convert CSV to Shapefile" /><published>2022-09-28T00:00:00+08:00</published><updated>2022-09-28T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2022/09/28/python-convert-csv-to-shapefile</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/09/28/python-convert-csv-to-shapefile/"><![CDATA[<h2 id="要准备"><a href="#要准备" class="headerlink" title="要准备"></a>要准备</h2>
<ul>
<li>pandas</li>
<li>geopandas</li>
</ul>
<h2 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h2>
<p>思想是这样的</p>
<ol>
<li>将要转化的csv文件放到List里</li>
<li>使用for循环遍历</li>
<li>在for循环内<ol>
<li>使用pandas读入csv</li>
<li>选取需要进行计算的列赋值给csvFileCal</li>
<li>将NaN设置为0（这是我此处的计算需要）</li>
<li>再将算出的结果添加到csvFile末尾，这样既保留了NaN的值，又计算出了正确的结果（因为在计算中如果具有NaN，计算结果也会是NaN）</li>
<li>将DataFrame转换为GeoDataFrame(这里仅有点对象，如果你还包含面对象的话还需要考虑别的方法)</li>
<li>设定好坐标系为WGS84(espg:4326)</li>
<li>调用GeoPandas的方法输出为shapefile文件</li>
</ol>
</li>
</ol>
<pre class="line-numbers language-python" data-language="python"><code class="language-python"><span class="token keyword">import</span> pandas <span class="token keyword">as</span> pd
<span class="token keyword">import</span> geopandas <span class="token keyword">as</span> gpd

<span class="token comment"># The code below is used to convert the csv file into a shapefile.</span>
nameList <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'india&amp;Pakistan91_95'</span><span class="token punctuation">,</span> <span class="token string">'india&amp;Pakistan96_00'</span><span class="token punctuation">,</span> <span class="token string">'india&amp;Pakistan01_05'</span><span class="token punctuation">,</span> <span class="token string">'india&amp;Pakistan06_10'</span><span class="token punctuation">,</span> <span class="token string">'india&amp;Pakistan11_15'</span><span class="token punctuation">,</span> <span class="token string">'india&amp;Pakistan16_20'</span><span class="token punctuation">]</span>

<span class="token keyword">for</span> name <span class="token keyword">in</span> nameList<span class="token punctuation">:</span>
    csvpath <span class="token operator">=</span> <span class="token string">r"D:\Desktop\StuInnovate\data\GTD\筛选\{}.csv"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span>
    csvFile <span class="token operator">=</span> pd<span class="token punctuation">.</span>read_csv<span class="token punctuation">(</span>csvpath<span class="token punctuation">)</span>
    
    csvFileCal <span class="token operator">=</span> csvFile<span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">'latitude'</span><span class="token punctuation">,</span> <span class="token string">'longitude'</span><span class="token punctuation">,</span><span class="token string">'nkill'</span><span class="token punctuation">,</span><span class="token string">'nwound'</span><span class="token punctuation">,</span><span class="token string">'property'</span><span class="token punctuation">,</span><span class="token string">'nhostkid'</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
    csvFileCal<span class="token punctuation">.</span>fillna<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> inplace<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span> <span class="token comment">#将NaN设置为0</span>
    
    csvFileCal<span class="token punctuation">[</span><span class="token string">'severeIndex'</span><span class="token punctuation">]</span> <span class="token operator">=</span> csvFileCal<span class="token punctuation">[</span><span class="token string">'nkill'</span><span class="token punctuation">]</span> <span class="token operator">+</span> csvFileCal<span class="token punctuation">[</span><span class="token string">'nwound'</span><span class="token punctuation">]</span> <span class="token operator">+</span> csvFileCal<span class="token punctuation">[</span><span class="token string">'property'</span><span class="token punctuation">]</span> <span class="token operator">+</span> csvFileCal<span class="token punctuation">[</span><span class="token string">'nhostkid'</span><span class="token punctuation">]</span>
    
    csvFile<span class="token punctuation">[</span><span class="token string">'severeIndex'</span><span class="token punctuation">]</span> <span class="token operator">=</span> csvFileCal<span class="token punctuation">[</span><span class="token string">'severeIndex'</span><span class="token punctuation">]</span>
    geoGDF <span class="token operator">=</span> gpd<span class="token punctuation">.</span>GeoDataFrame<span class="token punctuation">(</span>csvFile<span class="token punctuation">,</span> geometry<span class="token operator">=</span>gpd<span class="token punctuation">.</span>points_from_xy<span class="token punctuation">(</span>csvFile<span class="token punctuation">.</span>longitude<span class="token punctuation">,</span> csvFile<span class="token punctuation">.</span>latitude<span class="token punctuation">)</span><span class="token punctuation">)</span>
    geoGDF<span class="token punctuation">.</span>crs <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">'init'</span><span class="token punctuation">:</span> <span class="token string">'epsg:4326'</span><span class="token punctuation">}</span>
    geoGDF<span class="token punctuation">.</span>to_file<span class="token punctuation">(</span><span class="token string">r"D:\Desktop\StuInnovate\data\GTD\shp\{}.shp"</span><span class="token punctuation">.</span><span class="token builtin">format</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">,</span> driver<span class="token operator">=</span><span class="token string">'ESRI Shapefile'</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="GIS" /><category term="编程" /><category term="Python" /><summary type="html"><![CDATA[要准备 pandas geopandas 具体实现 思想是这样的 将要转化的csv文件放到List里 使用for循环遍历 在for循环内 使用pandas读入csv 选取需要进行计算的列赋值给csvFileCal 将NaN设置为0（这是我此处的计算需要） 再将算出的结果添加到csvFile末尾，这样既保留了NaN的值，又计算出了正确的结果（因为在计算中如果具有NaN，计算结果也会是NaN） 将DataFrame转换为GeoDataFrame(这里仅有点对象，如果你还包含面对象的话还需要考虑别的方法) 设定好坐标系为WGS84(espg:4326) 调用GeoPandas的方法输出为shapefile文件 import pandas as pd import geopandas as gpd]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/1920px-Pandas_logo.svg.png" /><media:content medium="image" url="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/1920px-Pandas_logo.svg.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">网页广告太多？UblockOrigin来帮你！</title><link href="https://zhanlutuzi.github.io/2022/09/15/use-ublock-origin/" rel="alternate" type="text/html" title="网页广告太多？UblockOrigin来帮你！" /><published>2022-09-15T00:00:00+08:00</published><updated>2022-09-15T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2022/09/15/use-ublock-origin</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/09/15/use-ublock-origin/"><![CDATA[<h1 id="你需要做的"><a href="#你需要做的" class="headerlink" title="你需要做的"></a>你需要做的</h1>
<ul>
<li>下载Ublock Origin<ul>
<li><a target="_blank" rel="noopener" href="https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en">chrome</a></li>
<li><a target="_blank" rel="noopener" href="https://microsoftedge.microsoft.com/addons/detail/ublock-origin/odfafepnkmbhccpbejgmiehpchacaeak">edge</a></li>
<li><a target="_blank" rel="noopener" href="https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/">firefox</a></li>
</ul>
</li>
<li>打开设置，添加规则</li>
</ul>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915192250262.png" /></p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915192342923.png" /></p>
<ul>
<li><p>添加常用规则，这里提供几个链接</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://raw.githubusercontent.com/easylist/easylistchina/master/easylistchina.txt">https://raw.githubusercontent.com/easylist/easylistchina/master/easylistchina.txt</a></p>
<p><a target="_blank" rel="noopener" href="https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjx-ublock.txt">https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjx-ublock.txt</a></p>
</blockquote>
</li>
</ul>
<p>这样就成功啦！</p>
<p>我们现在测试一下广告屏蔽的效果</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915193013229.png" alt="开启之前" /></p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915193048522.png" alt="开启之后" /></p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915193138538.png" alt="谷歌同理" /></p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915193206741.png" alt="屏蔽后" /></p>
<p>亲测各大视频网站的片头广告也被去除了！</p>
<p>Enjoy :)</p>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="Ublock Origin" /><summary type="html"><![CDATA[你需要做的 下载Ublock Origin chrome edge firefox 打开设置，添加规则 添加常用规则，这里提供几个链接 https://raw.githubusercontent.com/easylist/easylistchina/master/easylistchina.txt https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjx-ublock.txt 这样就成功啦！ 我们现在测试一下广告屏蔽的效果 亲测各大视频网站的片头广告也被去除了！ Enjoy :)]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915193637417.png" /><media:content medium="image" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220915193637417.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">VS2019更换字体进行美化</title><link href="https://zhanlutuzi.github.io/2022/08/22/things-about-vs-beauty/" rel="alternate" type="text/html" title="VS2019更换字体进行美化" /><published>2022-08-22T00:00:00+08:00</published><updated>2022-08-22T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2022/08/22/things-about-vs-beauty</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/08/22/things-about-vs-beauty/"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1>
<p>​        Jetbrains家的字体一直很赏心悦目，想在VS开发中也用到它；逛了逛官网，发现Jetbrains Mono font是开源的。</p>
<p>​        你可以在<a target="_blank" rel="noopener" href="https://www.jetbrains.com/lp/mono/">这个链接中下载。</a></p>
<h1 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h1>
<ol>
<li><p>在上面链接中下载字体，解压后选中你要安装的ttf文件，右键菜单栏中选择安装</p>
</li>
<li><p>打开 Visual studio 进入设置页面，将首页Windows渲染功能关闭</p>
</li>
</ol>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/devenv_YSRlQMbvbf.png" /></p>
<ol start="3">
<li>在字体选项栏中选择你要使用的字体</li>
</ol>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220822155718004.png" /></p>
<ol start="4">
<li>Enjoy Coding XD!</li>
</ol>
<h1 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h1>
<p><a target="_blank" rel="noopener" href="https://kinsta.com/blog/best-programming-fonts/">15 Best Programming Fonts for Better Coding</a></p>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="技巧" /><summary type="html"><![CDATA[前言 ​ Jetbrains家的字体一直很赏心悦目，想在VS开发中也用到它；逛了逛官网，发现Jetbrains Mono font是开源的。 ​ 你可以在这个链接中下载。 步骤 在上面链接中下载字体，解压后选中你要安装的ttf文件，右键菜单栏中选择安装 打开 Visual studio 进入设置页面，将首页Windows渲染功能关闭 在字体选项栏中选择你要使用的字体 Enjoy Coding XD! 附录 15 Best Programming Fonts for Better Coding]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://1000logos.net/wp-content/uploads/2020/08/Visual-Studio-Logo.png" /><media:content medium="image" url="https://1000logos.net/wp-content/uploads/2020/08/Visual-Studio-Logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">配置QGIS开发环境流程</title><link href="https://zhanlutuzi.github.io/2022/08/15/how-to-inplement-a-qgis-develop-environment/" rel="alternate" type="text/html" title="配置QGIS开发环境流程" /><published>2022-08-15T00:00:00+08:00</published><updated>2022-08-15T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2022/08/15/how-to-inplement-a-qgis-develop-environment</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/08/15/how-to-inplement-a-qgis-develop-environment/"><![CDATA[<h1 id="配置流程"><a href="#配置流程" class="headerlink" title="配置流程"></a>配置流程</h1>
<p>首先参考<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_44894692/article/details/119965203">这篇博文</a></p>
<p>完成它的流程之后，需要注意的是，要在项目设置中修改</p>
<p>不要把dll都拷贝到目录下，右键项目-属性-调试-环境中依次加入dll所在的目录，比如：<br />PATH=E:\Development\QGIS_3.24.2\bin;E:\Development\QGIS_3.24.2\apps\Qt5\bin;E:\Development\QGIS_3.24.2\apps\qgis\bin;E:\Development\QGIS_3.24.2\share\gdal<br />注意：取消勾选 左下角的从父级或项目默认设置继承选项。</p>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="技巧" /><summary type="html"><![CDATA[配置流程 首先参考这篇博文 完成它的流程之后，需要注意的是，要在项目设置中修改 不要把dll都拷贝到目录下，右键项目-属性-调试-环境中依次加入dll所在的目录，比如：PATH=E:\Development\QGIS_3.24.2\bin;E:\Development\QGIS_3.24.2\apps\Qt5\bin;E:\Development\QGIS_3.24.2\apps\qgis\bin;E:\Development\QGIS_3.24.2\share\gdal注意：取消勾选 左下角的从父级或项目默认设置继承选项。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/QGIS_logo%2C_2017.svg/1200px-QGIS_logo%2C_2017.svg.png" /><media:content medium="image" url="https://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/QGIS_logo%2C_2017.svg/1200px-QGIS_logo%2C_2017.svg.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Best Setup for Visual Studio</title><link href="https://zhanlutuzi.github.io/2022/08/10/best-setup-for-visual-studio/" rel="alternate" type="text/html" title="Best Setup for Visual Studio" /><published>2022-08-10T00:00:00+08:00</published><updated>2022-08-10T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2022/08/10/best-setup-for-visual-studio</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/08/10/best-setup-for-visual-studio/"><![CDATA[<h1 id="Best-Setup-For-Visual-studio"><a href="#Best-Setup-For-Visual-studio" class="headerlink" title="Best Setup For Visual studio"></a>Best Setup For Visual studio</h1>
<iframe width="640" height="360" src="https://www.youtube.com/embed/qeH9Xv_90KM">
</iframe>

<ul>
<li>Filter is not folder  That’s virtual folder!</li>
<li>We use real folder</li>
<li>src folder—-&gt;store my src code</li>
<li>VS button Show All Folder</li>
</ul>
<p>Debug folder?</p>
<ul>
<li>Change that setting!</li>
<li>all configuration   all platforms</li>
<li>Output directory —&gt;<code> $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)</code></li>
<li>Intermediates Directory —&gt;<code>$(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\$(ProjectName)</code></li>
<li>go to edit you can see any macro refer to!  (The same)</li>
</ul>
<h1 id="Pointer-in-C"><a href="#Pointer-in-C" class="headerlink" title="Pointer in C++"></a>Pointer in C++</h1>
<p>A pointer is just a integer which hold address!</p>
<h1 id="Reference-in-C"><a href="#Reference-in-C" class="headerlink" title="Reference in C++"></a>Reference in C++</h1>
<p>a reference could be a other name of a variable</p>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="技巧" /><summary type="html"><![CDATA[Best Setup For Visual studio]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://1000logos.net/wp-content/uploads/2020/08/Visual-Studio-Logo.png" /><media:content medium="image" url="https://1000logos.net/wp-content/uploads/2020/08/Visual-Studio-Logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">计算机组成体系结构复习笔记</title><link href="https://zhanlutuzi.github.io/2022/06/29/computerassemble/" rel="alternate" type="text/html" title="计算机组成体系结构复习笔记" /><published>2022-06-29T00:00:00+08:00</published><updated>2022-06-29T00:00:00+08:00</updated><id>https://zhanlutuzi.github.io/2022/06/29/computerassemble</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/06/29/computerassemble/"><![CDATA[<h1 id="整个学习的结构"><a href="#整个学习的结构" class="headerlink" title="整个学习的结构"></a>整个学习的结构</h1>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623164137961.png" /></p>
<h1 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a>寄存器</h1>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/%E5%AF%84%E5%AD%98%E5%99%A8.jpg" alt="MIPS中的寄存器" style="zoom: 25%;" /></p>

<p>MIPS所采用的寄存器如上图所示，下面列出几个常用的寄存器</p>
<ul>
<li><code>$zero</code>  存储唯一值——<code>0</code></li>
<li><code>$a0-\$a3</code> 这三个寄存器存储函数参数</li>
<li><code>$t0-\$t7</code>、<code>$t8-\$t9</code> 存储临时变量；注意这两组寄存器并不相连</li>
<li><code>$s0-\$s7</code> 用于保存变量的值</li>
<li><code>$sp</code> 保存栈指针；指向栈顶位置</li>
<li><code>$ra</code> 存储下一条需要执行指令的地址</li>
</ul>
<h1 id="指令自查表"><a href="#指令自查表" class="headerlink" title="指令自查表"></a>指令自查表</h1>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220622231329255.png" /></p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220622231427035.png" /></p>
<h1 id="汇编语言与汇编指令"><a href="#汇编语言与汇编指令" class="headerlink" title="汇编语言与汇编指令"></a>汇编语言与汇编指令</h1>
<h2 id="常用的MIPS指令"><a href="#常用的MIPS指令" class="headerlink" title="常用的MIPS指令"></a>常用的MIPS指令</h2>
<p><font color="green"><em><strong>算术运算</strong></em></font></p>
<hr />

<h3 id="add"><a href="#add" class="headerlink" title="add"></a>add</h3>
<p>加法</p>
<p><code>add $Destination,$num1,$num2</code></p>
<p>上述等价于—&gt; Destination = num1 +num2</p>
<h3 id="sub"><a href="#sub" class="headerlink" title="sub"></a>sub</h3>
<p>减法</p>
<p><code>sub $Destination,$num1,$num2</code></p>
<p>上述等价于—&gt; Destination = num1 - num2</p>
<h3 id="addi"><a href="#addi" class="headerlink" title="addi"></a>addi</h3>
<p>加立即数immediate</p>
<p><code>addi $Destination,$num1,num2</code></p>
<p>num2是一个数字而非寄存器</p>
<blockquote>
<p>📌注意！没有subi；subi可以用addi加负数代替</p>
</blockquote>
<h3 id="addu-amp-subu"><a href="#addu-amp-subu" class="headerlink" title="addu&amp;subu"></a>addu&amp;subu</h3>
<p>不检测溢出的加减法</p>
<p><code>addu $Destination,$num1,$num2</code></p>
<p><code>subu $Destination,$num1,$num2</code></p>
<blockquote>
<p>Ada检测溢出、C不检测溢出</p>
</blockquote>
<p>因此，编译器对于溢出的会按照语言的不同来转换不同的机器指令</p>
<p>addu和subu就是不检测溢出的指令</p>
<p><font color="green"><em><strong>内存相关数据传送</strong></em></font></p>
<hr />

<h3 id="lw"><a href="#lw" class="headerlink" title="lw"></a>lw</h3>
<p>LoadWord 从内存中加载一个<strong>字</strong>(通常是32位)</p>
<p><code>lw $t0,offset($s0)</code></p>
<p>表示从$s0指向的内存地址加offset偏移量后取得32位数据存入寄存器t0</p>
<blockquote>
<p>此处的$s0实际上存储的是一个指针</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220621210111283.png" alt="示意图" /></p>
<p>上图中内存指针指向1B;在1B的基础上+offset，以此为起始位置，取一个字放入$t0</p>
<h3 id="sw"><a href="#sw" class="headerlink" title="sw"></a>sw</h3>
<p>StoreWord 向内存中存储一个<strong>字</strong>(通常是32位)</p>
<p><code>sw $t0,offset($s0)</code></p>
<p>和上图原理一致，只是方向相反，取$t0的值，在$0指向的内存地址上+offset作为起始地址存储一个Word</p>
<blockquote>
<p><strong>字对齐</strong>：相邻字之间的地址相差四个字节而不是一个字节</p>
</blockquote>
<h3 id="lb-amp-sb"><a href="#lb-amp-sb" class="headerlink" title="lb&amp;sb"></a>lb&amp;sb</h3>
<p>LoadByte</p>
<p><code>lb $s0,3($s1) </code></p>
<p>从$s1+3的地址开始取一个Byte的数据存入$s0</p>
<blockquote>
<p>🤦‍♂️这里出现问题了，寄存器有4Byte，要存入1Byte怎么存放呢？</p>
<p>答案是将这8bits存到寄存器的低8位，高24位 <strong>符号位扩展</strong></p>
</blockquote>
<ul>
<li>对于有符号数<ul>
<li>符号位扩展：高24位与符号位相同（lb默认操作）</li>
</ul>
</li>
<li>对于无符号数<ul>
<li>零扩展：高24位均取0</li>
<li>有一条特殊指令 <code>lbu</code> LoadByteUnsighed;该指令会对高位进行零扩展</li>
</ul>
</li>
</ul>
<p>StoreByte</p>
<p><code>sb $s0,3($s1) </code></p>
<p>从$s0中取一个Byte存入内存中$s1+3的位置</p>
<blockquote>
<p>😶32位寄存器中取8位存入，那剩下的24位怎么办？</p>
</blockquote>
<p><font color="green"><em><strong>比较指令</strong></em></font></p>
<hr />

<h3 id="beq"><a href="#beq" class="headerlink" title="beq"></a>beq</h3>
<p>相等</p>
<p><code>$beq $reg1,$reg2,Label</code></p>
<p>当 reg1==reg2 时跳转到 Label 处</p>
<h3 id="bne"><a href="#bne" class="headerlink" title="bne"></a>bne</h3>
<p>不相等</p>
<p><code>$bne $reg1,$reg2,Label</code></p>
<p>当 reg1 != reg2 时跳转到 Label 处</p>
<h3 id="j"><a href="#j" class="headerlink" title="j"></a>j</h3>
<p>无条件跳转</p>
<p><code>j Label</code></p>
<p>跳转到 label处</p>
<h3 id="slt"><a href="#slt" class="headerlink" title="slt"></a>slt</h3>
<p>Set on less than;</p>
<p><code>slt reg1,reg2,reg3</code></p>
<ul>
<li>reg2&lt;reg3  reg1=1</li>
<li>否则 reg1=0</li>
</ul>
<blockquote>
<p>使用这一条指令，可以实现<code>＜</code>、<code>＞</code>、<code>≤</code>、<code>≥</code>的判断</p>
</blockquote>
<p>小于</p>
<pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">if (g&lt;h) goto Less; #变量映射是 g:$s0,h:$s1
------------&gt;
slt $t0,$s0,$s1
bne $t0,$0,Less<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<p>大于等于</p>
<pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">if (g≥h) goto GTE; #变量映射是 g:$s0,h:$s1
------------&gt;
slt $t0,$s0,$s1
beq $t0,$0,GTE<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<p>小于等于</p>
<pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">if (g≤h) goto LTE; #变量映射是 g:$s0,h:$s1
------------&gt;
slt $t0,$s1,$s0
beq $t0,$0,LTE<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<p>大于</p>
<pre class="line-numbers language-assembly" data-language="assembly"><code class="language-assembly">if (g＞h) goto LTE; #变量映射是 g:$s0,h:$s1
------------&gt;
slt $t0,$s1,$s0
bne $t0,$0,LTE<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<blockquote>
<p>👉还有立即数比较的<code>slti</code></p>
<p>无符号比较的<code>sltu</code></p>
</blockquote>
<p><font color="green"><em><strong>跳转指令</strong></em></font></p>
<hr />

<h3 id="jr"><a href="#jr" class="headerlink" title="jr"></a>jr</h3>
<p>Jump Regster 直接跳转寄存器</p>
<p><code>jr $reg</code></p>
<p>最常用的就是直接跳转到 $ra 即下一行要执行指令的位置</p>
<h3 id="jal"><a href="#jal" class="headerlink" title="jal"></a>jal</h3>
<p>Jump and Link 跳转并保存</p>
<p><code>jal Label</code></p>
<ol>
<li>Link 保存下一条指令的地址到$ra</li>
<li>Jump 跳转到给定标记处</li>
</ol>
<p><font color="green"><em><strong>逻辑运算</strong></em></font></p>
<hr />

<h3 id="and"><a href="#and" class="headerlink" title="and"></a>and</h3>
<p>按位与运算</p>
<p><code>and $reg1,$reg2,$reg3</code></p>
<p>reg1=reg2&amp;reg3</p>
<h3 id="andi"><a href="#andi" class="headerlink" title="andi"></a>andi</h3>
<p>与立即数进行与运算</p>
<p><code>and $reg1,$reg2,immediate</code></p>
<p>reg1=reg2&amp;immediate</p>
<blockquote>
<p>⭐使用掩码做与运算将位串的特定部分分离出来</p>
<p>1110&amp;0011 = 0010 即分离出了后两位</p>
</blockquote>
<h3 id="or"><a href="#or" class="headerlink" title="or"></a>or</h3>
<p>按位与运算</p>
<p><code>or $reg1,$reg2,$reg3</code></p>
<p>reg1=reg2&amp;reg3</p>
<h3 id="ori"><a href="#ori" class="headerlink" title="ori"></a>ori</h3>
<p>与立即数进行与运算</p>
<p><code>ori $reg1,$reg2,immediate</code></p>
<p>reg1=reg2|immediate</p>
<blockquote>
<p>⭐任何数与1做或为1，与0做或为原数—&gt;强制某些位为1</p>
</blockquote>
<h3 id="sll"><a href="#sll" class="headerlink" title="sll"></a>sll</h3>
<p>Shift Left Logical 逻辑左移</p>
<p><code>sll $reg1,$reg2,num</code></p>
<p>将reg2里的值左移num位放入reg1中</p>
<p><strong>空出来的位填0</strong></p>
<h3 id="srl"><a href="#srl" class="headerlink" title="srl"></a>srl</h3>
<p>和sll类似，不过右移num位</p>
<p><strong>空出来的位填0</strong></p>
<h3 id="sra"><a href="#sra" class="headerlink" title="sra"></a>sra</h3>
<p>Shift Right Arithmetic 算术右移</p>
<p>左移num位就是$ ×2^{num}$</p>
<p>右移$ ×2^{-num}$，或者说$ ÷2^{num}$</p>
<blockquote>
<p>📌要做计算的时候记得用sra</p>
</blockquote>
<h2 id="函数调用"><a href="#函数调用" class="headerlink" title="函数调用"></a>函数调用</h2>
<p>简单的嵌套调用可以使用寄存器约定；但是复杂的多层嵌套调用就必须<u><strong>使用栈</strong></u>了！</p>
<h3 id="寄存器约定保存信息"><a href="#寄存器约定保存信息" class="headerlink" title="寄存器约定保存信息"></a>寄存器约定保存信息</h3>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220622162149577.png" alt="寄存器约定" /></p>
<h3 id="使用栈保存信息"><a href="#使用栈保存信息" class="headerlink" title="使用栈保存信息"></a>使用栈保存信息</h3>
<ul>
<li>手工编译</li>
</ul>
<h2 id="Caller-amp-Callee"><a href="#Caller-amp-Callee" class="headerlink" title="Caller&amp;Callee"></a>Caller&amp;Callee</h2>
<h1 id="机器语言"><a href="#机器语言" class="headerlink" title="机器语言"></a>机器语言</h1>
<ul>
<li>I-格式<ul>
<li>带立即数</li>
<li>lw&amp;sw</li>
<li>分支语句</li>
</ul>
</li>
<li>J-格式<ul>
<li>j&amp;jal</li>
</ul>
</li>
<li>R-格式<ul>
<li>其他所有指令</li>
</ul>
</li>
</ul>
<h2 id="指令格式"><a href="#指令格式" class="headerlink" title="指令格式"></a>指令格式</h2>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623085820480.png" /></p>
<h3 id="R格式"><a href="#R格式" class="headerlink" title="R格式"></a>R格式</h3>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220622223829709.png" /></p>
<ul>
<li>opcode: 所有R格式指令，此字段为0；和funct字段一起，共同确定具体的指令</li>
<li>rs <code>Source Register</code> 确定首操作数的寄存器</li>
<li>rt <code>Target Register</code> 确定次操作数的寄存器</li>
<li>rd <code>Destination Register</code> 指定存放计算结果的寄存器</li>
<li>shamt 包含移位指令需要移的位数；除了移位指令外，此位设为0</li>
<li><strong>add $reg1(rd),$reg2(rs),$reg3(rt)</strong></li>
</ul>
<h3 id="I格式"><a href="#I格式" class="headerlink" title="I格式"></a>I格式</h3>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220622230759838.png" /></p>
<ul>
<li>opcode 独立指定指令</li>
<li>rs 指定寄存器操作数（如果有）</li>
<li>rt 指定保存计算结果的寄存器</li>
<li><strong>addi $reg1(rt) $reg2(rs) imme</strong></li>
</ul>
<blockquote>
<p>　　addi,slti  立即数<font color="green">符号扩展</font>为32位</p>
</blockquote>
<blockquote>
<p>❓这里无符号数也进行符号位扩展，为了硬件的简单牺牲一定的数据，仅对$2^{15}\le $n$\lt 2^{16}$有问题，只能由汇编器想办法解决了</p>
<p><font color="red">I格式问题</font></p>
</blockquote>
<p>如何解决I格式问题？<u><em><strong>软件中处理+新指令</strong></em></u></p>
<h4 id="lui"><a href="#lui" class="headerlink" title="lui"></a>lui</h4>
<p>新指令<code>lui register,immediate</code></p>
<p>Load Upper Immediate  装入立即数高位</p>
<p>取立即数并将立即数放到寄存器高位部分，剩下部分填充0</p>
<h4 id="PC-相对寻址"><a href="#PC-相对寻址" class="headerlink" title="PC-相对寻址"></a>PC-相对寻址</h4>
<blockquote>
<p>我们只有16位立即数，怎么解决32位分支指令？</p>
</blockquote>
<p><strong>PC-相对寻址</strong>作为32位分支指令解决方案</p>
<p>以PC为基点，分支$\pm 2^{15} $字节，保证大多数循环的寻址要求</p>
<blockquote>
<p>指令是字，满足字对齐，后两位总是00</p>
</blockquote>
<ul>
<li>指定立即数immmediate以<font color="red">字</font>作为单位</li>
</ul>
<p>这样以PC为基点，可以分支$\pm 2^{15} $个字 (或$\pm 2^{17} $个字节)，因此，可以处理的循环范围为原来的4倍</p>
<h3 id="J格式"><a href="#J格式" class="headerlink" title="J格式"></a>J格式</h3>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623084102888.png" /></p>
<p>现在可以表示26位的地址</p>
<ul>
<li>由于字对齐，默认最后两位为00    +2</li>
<li>由于PC相对寻址，从PC处取最高4位     +4</li>
<li>这样26+2+4=32；我们成功表达了32位的地址</li>
</ul>
<h2 id="反汇编"><a href="#反汇编" class="headerlink" title="反汇编"></a>反汇编</h2>
<p>对于32位机器指令的解码遵循下面步骤</p>
<ol>
<li>把16进制转为2进制表示</li>
<li>确定opcode和指令格式<ul>
<li>opcode R:0   J:2/3    I:其他</li>
</ul>
</li>
<li>基于格式分割字段<ul>
<li>R:6 5 5 5 5 6</li>
<li>I:6 5 5 16</li>
<li>J:6 26</li>
</ul>
</li>
<li>反汇编为MIPS汇编指令</li>
</ol>
<h2 id="伪指令"><a href="#伪指令" class="headerlink" title="伪指令"></a>伪指令</h2>
<blockquote>
<p>不能直接转成MIPS语言，先要转成几条MIPS指令的组合</p>
</blockquote>
<p>MAL(MIPS Assembly Language) 包含伪指令</p>
<p>TAL(True Assembly Language) 不包含伪指令</p>
<p><code>C---&gt;MAL---&gt;TAL</code>翻译顺序</p>
<h3 id="一些常见的伪指令"><a href="#一些常见的伪指令" class="headerlink" title="一些常见的伪指令"></a>一些常见的伪指令</h3>
<h4 id="move"><a href="#move" class="headerlink" title="move"></a>move</h4>
<p>寄存器赋值</p>
<p><code>move $reg2,$reg1</code></p>
<p>将reg1的值赋给reg2</p>
<h4 id="li"><a href="#li" class="headerlink" title="li"></a>li</h4>
<p>装入立即数（Load Immediate）</p>
<p><code>li $reg,immediate</code></p>
<h4 id="la"><a href="#la" class="headerlink" title="la"></a>la</h4>
<p>装入地址（Load Address）</p>
<p><code>la $reg,label</code></p>
<p>把label对应的地址装入reg</p>
<h4 id="ror"><a href="#ror" class="headerlink" title="ror"></a>ror</h4>
<p>循环右移（Rotate Right Instruction）</p>
<p><code>ror $reg,value</code></p>
<p>把寄存器中的值循环右移value位</p>
<h4 id="错误操作的操作数"><a href="#错误操作的操作数" class="headerlink" title="错误操作的操作数"></a>错误操作的操作数</h4>
<p>比如addu指令写成了<code>addu $reg1,$reg2,immediate</code></p>
<p>会自动改写为<code>addiu $reg1,$reg2,immediate</code></p>
<h1 id="浮点数"><a href="#浮点数" class="headerlink" title="浮点数"></a>浮点数</h1>
<p>IEEE754标准</p>
<p>标准形式</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623101707172.png" /></p>
<p>通过一个例子感受如何在二进制位串和十进制数字间进行转换</p>
<blockquote>
<p>0 |0110 1000|101 0101 0100 0011 0100 0010</p>
</blockquote>
<p>将上面的位串转换为十进制</p>
<ol>
<li>s=0 正数</li>
<li>expont = 104   偏移调整104-127=-23</li>
<li>significand = 1+1*$2^{-1}$+0*$2^{-2}$+1*$2^{-3}$+…+0*$2^{-23}$ = 1+0.666115</li>
<li>1.666115*$2^{-23}$ = 1.9860*$10^{-7}$</li>
</ol>
<p>十进制转二进制</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623150050510.png" /></p>
<h2 id="特殊数的表示"><a href="#特殊数的表示" class="headerlink" title="特殊数的表示"></a>特殊数的表示</h2>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/QQ%E5%9B%BE%E7%89%8720220623151811.jpg" /></p>
<p>精度</p>
<ul>
<li>Precision 计算机一个字用于表示数的位数</li>
</ul>
<p>精确性</p>
<ul>
<li>Accuracy 用于衡量精确值与计算机表示之间的差异</li>
</ul>
<h1 id="程序的运行"><a href="#程序的运行" class="headerlink" title="程序的运行"></a>程序的运行</h1>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623153109849.png" alt="image-20220623153109849" /></p>
<h2 id="编译和翻译"><a href="#编译和翻译" class="headerlink" title="编译和翻译"></a>编译和翻译</h2>
<p>高级语言的执行有两种方式</p>
<ul>
<li>解释Interpreter<ul>
<li>例如Python、Java；解释器读入语言并执行</li>
</ul>
</li>
<li>翻译Translator<ul>
<li>将程序翻译成机器语言再执行；执行效率高、隐藏源码</li>
</ul>
</li>
</ul>
<h2 id="汇编器"><a href="#汇编器" class="headerlink" title="汇编器"></a>汇编器</h2>
<h3 id="1-读入并使用指示器Directive"><a href="#1-读入并使用指示器Directive" class="headerlink" title="1.读入并使用指示器Directive"></a>1.读入并使用指示器Directive</h3>
<blockquote>
<p>指示器产生一系列的提示信息，不产生机器指令</p>
</blockquote>
<h3 id="2-替代伪指令"><a href="#2-替代伪指令" class="headerlink" title="2.替代伪指令"></a>2.替代伪指令</h3>
<p>将伪指令替换为一组汇编指令</p>
<h3 id="3-生成机器语言"><a href="#3-生成机器语言" class="headerlink" title="3.生成机器语言"></a>3.生成机器语言</h3>
<p>分支怎么办？PC相对寻址</p>
<blockquote>
<p> 前向引用问题？分支指令引用还未遇到的标记</p>
</blockquote>
<p>使用两次扫描的方式解决问题，一次扫描记住标记位置，第二次扫描使用标记位置产生代码。</p>
<blockquote>
<p>跳转指令的问题？需要绝对地址</p>
</blockquote>
<p>产生两张表</p>
<ul>
<li>符号表<ul>
<li>我有什么</li>
</ul>
</li>
<li>重定位表<ul>
<li>我要什么</li>
</ul>
</li>
</ul>
<h3 id="4-生成目标文件"><a href="#4-生成目标文件" class="headerlink" title="4.生成目标文件"></a>4.生成目标文件</h3>
<h2 id="连接器"><a href="#连接器" class="headerlink" title="连接器"></a>连接器</h2>
<p>输入代码和信息表；输出可执行程序</p>
<p>使得多个文件的分离编译称为可能</p>
<ol>
<li>从每个.o文件取代码段放一起</li>
<li>从每个.o文件取数据段放一起，然后整体连到代码段尾部</li>
<li>检查重定向表填充绝对地址<ul>
<li>PC相对寻址 不重定位</li>
<li>绝对地址、外部引用、数据引用 重定位</li>
</ul>
</li>
</ol>
<p>动态链接库DLL</p>
<p>​    便于及时更新，且体积小</p>
<p>静态链接库lib</p>
<h2 id="装入器"><a href="#装入器" class="headerlink" title="装入器"></a>装入器</h2>
<p>操作系统的一部分，输入执行代码(.out).exe，输出正在运行的程序</p>
<h1 id="电路基础和基本计算模块"><a href="#电路基础和基本计算模块" class="headerlink" title="电路基础和基本计算模块"></a>电路基础和基本计算模块</h1>
<h2 id="同步数字系统"><a href="#同步数字系统" class="headerlink" title="同步数字系统"></a>同步数字系统</h2>
<p>ISA(Instruction Structure Architecture,指令集结构)是软件与硬件之间的协议</p>
<ul>
<li>同步<ul>
<li>所有的操作都由一个中央时钟同步</li>
</ul>
</li>
<li>数字<ul>
<li>所有的值都用离散值来表示</li>
</ul>
</li>
</ul>
<p>晶体管的两种类型</p>
<ul>
<li>n-type<ul>
<li>有电通，无电不通</li>
</ul>
</li>
<li>p-type<ul>
<li>有电不通，无电通</li>
</ul>
</li>
</ul>
<h2 id="信号与波形"><a href="#信号与波形" class="headerlink" title="信号与波形"></a>信号与波形</h2>
<p>同步数字系统由两种基本电路构成</p>
<p><strong>信号正边沿触发！</strong></p>
<ul>
<li>组合逻辑电路<ul>
<li>输出是输入经过一定规则后的运算结果</li>
</ul>
</li>
<li>状态电路<ul>
<li>存储信息</li>
</ul>
</li>
</ul>
<h2 id="状态单元"><a href="#状态单元" class="headerlink" title="状态单元"></a>状态单元</h2>
<p>状态单元用来储存数值，控制组合逻辑块间的信息流动；如寄存器</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623204847692.png" /></p>
<p>寄存器由多个翻转器（在0,1之间变换）组成，正边沿触发；reset是强制清零的信号</p>
<p>可以通过增加寄存器加快时钟频率</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623205318997.png" /></p>
<p>状态变换FSM图也可以借助寄存器来实现</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623205459492.png" /></p>
<h2 id="电路组合"><a href="#电路组合" class="headerlink" title="电路组合"></a>电路组合</h2>
<p>常见逻辑门的表示与真值表</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623205742941.png" /></p>
<p>异或XOR:当有奇数个1时输出1</p>
<h3 id="布尔代数"><a href="#布尔代数" class="headerlink" title="布尔代数"></a>布尔代数</h3>
<p>运用布尔代数可以化简电路</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220624105533708.png" alt="运算律" /></p>
<p>结合真值表可以设计电路</p>
<p>如何从真值表生成门电路呢？看下图</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220623211653780.png" /></p>
<p>·是And  </p>
<p>+是Or</p>
<p>图5-25中显示的是二选一MUX；利用层次结构可用二选一的MUX组合成四选一的MUX</p>
<h3 id="设计ALU"><a href="#设计ALU" class="headerlink" title="设计ALU"></a>设计ALU</h3>
<p>32位加法器</p>
<p>首先画出真值表，发现有$2^{64}$个表项；太多了，我们得换个思路—-&gt;分解！</p>
<p>设计一位加法器</p>
<p>画出真值表—&gt;写出布尔表达式并化简—&gt;设计电路</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220624105748237.png" /></p>
<p>把许多一位加法器连起来就可以构成多位加法器</p>
<p><font color="red">溢出？不懂</font></p>
<p>减法器实际上只需要在加法器上做一点点改进就好</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220624110436334.png" /></p>
<h1 id="数据通道-控制通道"><a href="#数据通道-控制通道" class="headerlink" title="数据通道+控制通道"></a>数据通道+控制通道</h1>
<p>数据通道分为五步</p>
<ol>
<li>取指<ul>
<li>从内存中取出32位指令；PC=PC+4</li>
</ul>
</li>
<li>指令译码<ul>
<li>读opcode确定类型；分段；取出相关数据</li>
</ul>
</li>
<li>ALU<ul>
<li>实际计算</li>
</ul>
</li>
<li>内存访问<ul>
<li>只有lw、sw此阶段工作；利用cache加快访问</li>
</ul>
</li>
<li>写寄存器</li>
</ol>
<p>需要在脑海里能够画出下面这个完整版的数据通道</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220626122922492.png" /></p>
<p>控制信号的设计，其实就是opcode与funct做and运算，得到一串二进制数，这串二进制数做or运算得到所需的信号</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220626123223021.png" /></p>
<h1 id="流水线改进性能"><a href="#流水线改进性能" class="headerlink" title="流水线改进性能"></a>流水线改进性能</h1>
<p>延迟不变；增加吞吐量</p>
<h2 id="结构困境"><a href="#结构困境" class="headerlink" title="结构困境"></a>结构困境</h2>
<p>硬件不支持某些组合</p>
<ul>
<li>内存同时被访问<ul>
<li>设计两个内存：一个指令内存一个数据内存</li>
<li>建立两个一级缓存</li>
</ul>
</li>
<li>寄存器同时被读写<ul>
<li>前半段只写；后半段只读</li>
<li>构建新的reg寄存器支持同时读写</li>
</ul>
</li>
</ul>
<h2 id="控制困境"><a href="#控制困境" class="headerlink" title="控制困境"></a>控制困境</h2>
<p>要等分支语句的判断出来后才能进行下一步</p>
<ul>
<li>阻塞no-op消耗三个时钟周期<ul>
<li>优化1：插入特殊分支比较器，在第二阶段解码得出分支立即决策更新PC</li>
<li>优化2：重新定义分支；（无论是否分支，分支判断语句下一条总被执行，叫做分支延时槽Branch-Delay slot<ul>
<li>也就是将不被分支影响的指令放到分支语句后执行；重新排列汇编语句执行顺序</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="数据困境"><a href="#数据困境" class="headerlink" title="数据困境"></a>数据困境</h2>
<p>后方指令依赖前面指令的结果</p>
<ul>
<li>前馈解决<ul>
<li>添加额外硬件从ALU的计算结果中直接取出后项要用的数据</li>
<li>无法解决就阻塞+前馈<ul>
<li>硬件阻塞<ul>
<li>可以单独插入一个bubble在两阶段之间</li>
</ul>
</li>
<li>nop阻塞<ul>
<li>直接插入一个nop指令什么也不做</li>
</ul>
</li>
<li>load之后的指令叫load delay slot</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="Cache高速缓存"><a href="#Cache高速缓存" class="headerlink" title="Cache高速缓存"></a>Cache高速缓存</h1>
<p>利用时间局部性和空间局部性</p>
<p>Cache是主存的子集的一个拷贝</p>
<p>Cache与内存间传输数据的单位——块block</p>
<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220626181615411.png" alt="image-20220626181615411" /></p>
<p>如上图，内存地址被划分为三块<code>tag</code> <code>index  </code> <code>offset</code></p>
<p>offset长度根据（能表示一个块包含的字节数来决定的位数）</p>
<p>index长度等于（能表示（缓存大小÷一行缓存的大小）这个的位数）</p>
<p>tag = 地址位数-offset和index的位数</p>
<p>访问cache有三种情况</p>
<ol>
<li>命中</li>
<li>未命中</li>
<li>未命中且替换</li>
</ol>
<p>访问cache有这样三步</p>
<ol>
<li>看index确认cache行数</li>
<li>看tag确认是否是正确的行</li>
<li>看offset访问内容</li>
<li>如果2不满足就替换</li>
</ol>
<h2 id="内存读写"><a href="#内存读写" class="headerlink" title="内存读写"></a>内存读写</h2>
<p>更新内存的方法</p>
<ul>
<li><p>写内存Write Through</p>
<ul>
<li>同时写内存和cache</li>
</ul>
</li>
<li><p>写回WriteBack</p>
<ul>
<li>只写cache，替换内存中的字为dirty；当该块缓存被替换时写入内存</li>
</ul>
</li>
</ul>
<p>最小化平均访问时间AMAT（Average Memory Access Time）</p>
<p>AMAT=$HitTime+MissPenalty×MissRate$</p>
<p>块替换策略，常用LRU</p>]]></content><author><name>zhanlutuzi</name></author><category term="笔记" /><category term="笔记" /><category term="计算机组成体系" /><summary type="html"><![CDATA[整个学习的结构 寄存器]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220629211703502.png" /><media:content medium="image" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220629211703502.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">虚拟机安装MacOS一些问题的解决</title><link href="https://zhanlutuzi.github.io/2022/06/09/macos-vmware-question-solve/" rel="alternate" type="text/html" title="虚拟机安装MacOS一些问题的解决" /><published>2022-06-09T23:26:49+08:00</published><updated>2022-06-09T23:26:49+08:00</updated><id>https://zhanlutuzi.github.io/2022/06/09/macos-vmware-question-solve</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/06/09/macos-vmware-question-solve/"><![CDATA[<h1 id="前言">前言</h1>

<p>太久不更新了，<del>果然人类的本质就是鸽子吗</del></p>

<p>今天结束了数据库的上机，晚上不想写报告，开始折腾起来虚拟机了；起因是我之前写了篇虚拟机安装MacOS的文章👉<a href="https://zhanlutuzi.top/2022/02/13/hexo-bo-ke-da-jian/#toc-heading-16">链接</a>；但是安装上之后屏幕显示分辨率一直不正常，而且也没办法用代理链接Github，这就非常的没用了。今天折腾了一下，把问题解决了，写篇博客记录一下🖖</p>

<p>​																																			——某位马上要考六级期末一堆DDL的<del>摸鱼</del>大学生</p>

<h1 id="虚拟机代理配置">虚拟机代理配置</h1>

<p>一般来说，在虚拟机内部设置代理有这么几个办法：</p>

<ul>
  <li>像你用Windows一样在虚拟机内部装代理软件
    <ul>
      <li>不推荐，别用</li>
    </ul>
  </li>
  <li>借助VMware，共享主机的代理
    <ul>
      <li>优雅！ Gorgeous💃</li>
    </ul>
  </li>
</ul>

<p>那么我就介绍第二种办法，<del>要优雅XD</del></p>

<h2 id="vmware配置网络环境">VMWare配置网络环境</h2>

<p>打开虚拟网络编辑器</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609234728971.png" alt="打开虚拟网络编辑器" /></p>

<p>点击更改设置</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609234932710.png" alt="" /></p>

<p>如图所示加上VMnet8，并配置相关参数。</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609235050872.png" alt="其实这里加别的VMnet也行，配置一样就可以" /></p>

<p>接下来就对单独的虚拟机进行设置</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609235236761.png" alt="打开设置页面" /></p>

<p>更改网络设置</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609235335474.png" alt="网络链接改成VMnet8" /></p>

<p>到这里，只需要再获取VMnet8的ip地址就好；打开CMD，输入<code class="language-plaintext highlighter-rouge">ipconfig</code>找到VMnet8</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609235513899.png" alt="务必记住它，后面有用" /></p>

<h2 id="虚拟机内部设置">虚拟机内部设置</h2>

<p>打开你的虚拟机，这里用MacOS做演示；Ubuntu操作见<a href="https://www.jianshu.com/p/6c7abd4adc9b">链接</a></p>

<p>进入你的Mac虚拟机，打开设置里的网络选项，点击高级进入下图页面；请将我勾选的四项都设置为上面让你记住的ip地址<img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609235829077.png" alt="" /></p>

<p>端口号和你的代理软件相关，我这里使用的是Clash，端口就是上面那个箭头所指的位置；记得一定把<code class="language-plaintext highlighter-rouge">Allow LAN</code>打开，因为VMnet8走的就是LAN(Local Area Network)</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220610000058927.png" alt="" /></p>

<p>以上，代理设置完毕！愉快地使用 Github 吧！</p>

<h1 id="macos分辨率显示异常">MacOS分辨率显示异常</h1>

<p>在你按照我之前的教程安装完MacOS后一定会发现，这个分辨率怎么这么糊啊，而且还改不了，就很难受🤦‍♂️</p>

<p>下面我们着手解决这个问题</p>

<h2 id="安装vmwaretool">安装VMwareTool</h2>

<p>打开MacOS后，点击菜单栏里的<code class="language-plaintext highlighter-rouge">虚拟机</code>再点击<code class="language-plaintext highlighter-rouge">安装VMware Tool</code>，按照提示进行安装就行，详情请看👉<a href="https://www.geekrar.com/how-to-fix-macos-catalina-screen-resolution-on-vmware/">链接</a></p>

<p>具体步骤</p>

<ol>
  <li>安装VMwareTool</li>
  <li>提示需要权限，按照指引给权限</li>
  <li>重启</li>
  <li>再次安装</li>
  <li>再重启</li>
  <li>安装完毕</li>
</ol>

<h2 id="修改vmware设置">修改VMware设置</h2>

<p>打开首选项</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220610001618493.png" alt="" /></p>

<p>如图进行设置</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220610001707275.png" alt="" /></p>

<p>设置完后重启MacOS</p>

<p>点一下自由拉伸，然后随便拉伸一下虚拟机的窗口</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220610002009689.png" alt="" /></p>

<p>如果这样还没有用，就打开<code class="language-plaintext highlighter-rouge">Terminal</code></p>

<p>输入<code class="language-plaintext highlighter-rouge">/Library/Application\Support/VMware\ Tools/vmware-resolutionSet 1920 1080</code></p>

<p>之后再在设置里改一下显示比例</p>

<p>这个方法我没有使用，如果有问题可以参考👉<a href="https://www.zhihu.com/question/68703160">链接</a></p>

<h1 id="参考资料">参考资料</h1>

<p>https://www.zhihu.com/question/68703160</p>

<p>https://blog.51cto.com/sddai/3090254</p>

<p>https://www.jianshu.com/p/4384047334d1</p>

<p>https://www.geekrar.com/how-to-fix-macos-catalina-screen-resolution-on-vmware/</p>]]></content><author><name>zhanlutuzi</name></author><category term="教程" /><category term="VMware" /><category term="虚拟机" /><category term="MacOS" /><category term="教程" /><summary type="html"><![CDATA[前言]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609232953197.png" /><media:content medium="image" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220609232953197.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">操作系统学习笔记_03</title><link href="https://zhanlutuzi.github.io/2022/04/04/os-3-i-o/" rel="alternate" type="text/html" title="操作系统学习笔记_03" /><published>2022-04-04T14:52:13+08:00</published><updated>2022-04-04T14:52:13+08:00</updated><id>https://zhanlutuzi.github.io/2022/04/04/os-3-i-o</id><content type="html" xml:base="https://zhanlutuzi.github.io/2022/04/04/os-3-i-o/"><![CDATA[<h1 id="io-硬件原理">I/O 硬件原理</h1>

<h2 id="io-设备">I/O 设备</h2>

<p>I/O 设备可以分为：</p>

<ul>
  <li>块设备<code class="language-plaintext highlighter-rouge">Block Device</code>
    <ul>
      <li>设备将信息存储在固定大小的块中<code class="language-plaintext highlighter-rouge">如磁盘</code></li>
      <li>数据块大小一般为<code class="language-plaintext highlighter-rouge">512 Bytes--&gt;32768 Bytes</code></li>
      <li>每个块都能独立于其它块而读写</li>
    </ul>
  </li>
  <li>字符设备<code class="language-plaintext highlighter-rouge">Character Device</code>
    <ul>
      <li>设备发送或接收字符流</li>
      <li>无法编址，也就不存在寻址操作</li>
      <li>如<code class="language-plaintext highlighter-rouge">打印机、键盘、网络接口、鼠标等</code></li>
    </ul>
  </li>
  <li>其他
    <ul>
      <li>无法被分到块设备或字符设备中</li>
      <li>比如<code class="language-plaintext highlighter-rouge">时钟</code></li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>❗ I/O 设备在速度上差异很大，见下图，这为我们管理 I/O 设备提出了挑战，在后文中会提到解决办法。</p>
</blockquote>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404160027122.png" style="zoom:50%;" /></p>

<h2 id="设备控制器">设备控制器</h2>

<p>​	<strong>I/O设备组成中的电子部件</strong>被称为设备控制器，操作系统往往和设备的控制器打交道，大多数个人计算机采用<code class="language-plaintext highlighter-rouge">总线模型</code>进行 CPU 与控制器之间的交流。</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/ApplicationFrameHost_1vQQ1KaZ7j.png" alt="" /></p>

<p>​	大型机则采用其他模型，此类模型带有<code class="language-plaintext highlighter-rouge">I/O通道</code>，一种专用于输入输出工作的计算机。</p>

<h2 id="内存映射io">内存映射I/O</h2>

<blockquote>
  <p>❓ CPU如何控制寄存器与设备数据缓冲区进行通信？</p>
</blockquote>

<ul>
  <li>使用I/O端口
    <ul>
      <li>内存和I/O地址空间不同</li>
    </ul>
  </li>
  <li>内存映射I/O
    <ul>
      <li>I/O寄存器是内存空间的一部分</li>
    </ul>
  </li>
</ul>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404193104304.png" alt="" /></p>

<h2 id="中断">中断</h2>

<p>大部分I/O接口提供一个输出用于驱动<code class="language-plaintext highlighter-rouge">IRQ Interrupt ReQuest</code>线</p>

<p>即插即用技术使得 BIOS 在启动时为设备自动分配 IRQ 以避免冲突</p>

<h2 id="直接存储器存取-dma">直接存储器存取 DMA</h2>

<p><code class="language-plaintext highlighter-rouge">Direct Memory Access , DMA</code></p>

<p>它使得毋须占用CPU宝贵的时间去交换数据，利用 DMA 控制器调控多个设备的数据传送</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404193751362.png" alt="" /></p>

<h1 id="io-软件原理">I/O 软件原理</h1>

<h2 id="目标">目标</h2>

<ul>
  <li>设备无关性
    <ul>
      <li>访问I/O毋须指定设备</li>
    </ul>
  </li>
  <li>统一命名法</li>
  <li>错误处理</li>
  <li>同步/异步传输</li>
  <li>缓冲</li>
</ul>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404200109964.png" alt="" /></p>

<h2 id="设备驱动程序">设备驱动程序</h2>

<p>用来控制特定设备的一组特定的代码被称作设备驱动程序，驱动程序传统上是系统内核的一部分</p>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404201218694.png" alt="" /></p>

<h1 id="死锁">死锁</h1>

<h2 id="资源">资源</h2>

<ul>
  <li>可抢占资源
    <ul>
      <li>抢占以后没有副作用，如存储器</li>
    </ul>
  </li>
  <li>不可抢占资源
    <ul>
      <li>抢占后危害很大，比如CD刻录机</li>
      <li>死锁与不可抢占资源相关</li>
    </ul>
  </li>
</ul>

<h3 id="使用一个资源的顺序">使用一个资源的顺序</h3>

<ol>
  <li>请求资源</li>
  <li>使用资源</li>
  <li>释放资源</li>
</ol>

<h2 id="死锁的原理">死锁的原理</h2>

<blockquote>
  <p>一个进程集合中的每个进程都在等待本集合中其他进程才能引发的事件，那么这组进程是死锁的</p>
</blockquote>

<h3 id="死锁发生的四个必要条件">死锁发生的四个必要条件</h3>

<ul>
  <li>互斥条件
    <ul>
      <li>一个资源，要么被分配给一个进程，要么就是可用的</li>
    </ul>
  </li>
  <li>占有和等待条件
    <ul>
      <li>已分配到资源的进程可以请求新的资源</li>
    </ul>
  </li>
  <li>不可抢占条件
    <ul>
      <li>被分配的资源不能抢占</li>
    </ul>
  </li>
  <li>环路等待条件
    <ul>
      <li>环路中每个进程都在等待下一个进程占用的资源</li>
    </ul>
  </li>
</ul>

<h3 id="死锁模型">死锁模型</h3>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404202545162.png" alt="" /></p>

<h3 id="处理死锁的四个策略">处理死锁的四个策略</h3>

<ul>
  <li>忽略该问题
    <ul>
      <li>鸵鸟算法</li>
    </ul>
  </li>
  <li>检测死锁并恢复</li>
  <li>谨慎地对资源进行动态分配</li>
  <li>破坏上述四个必要条件之一</li>
</ul>

<h2 id="死锁的预防">死锁的预防</h2>

<ul>
  <li>破坏互斥使用条件</li>
  <li>禁止已拥有资源的进程等待其他资源</li>
  <li>给所有资源提供一个全局编号，请求需要按编号顺序提出</li>
</ul>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404204123554.png" alt="" /></p>

<h2 id="避免死锁">避免死锁</h2>

<p><strong>&lt;font color=red size=4.5 face=仿宋&gt;通过仔细分配资源来避免死锁&lt;/font&gt;</strong></p>

<h3 id="单一资源的银行家算法">单一资源的银行家算法</h3>

<p>在进程申请资源的时候，判断本次申请是否安全，如果安全再分配，就像银行借贷款。</p>

<h3 id="资源轨迹">资源轨迹</h3>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404205750270.png" alt="" /></p>

<p>上图中，阴影区域内就是资源发生冲突的区域</p>

<h3 id="多种资源的银行家算法">多种资源的银行家算法</h3>

<p><img src="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220404210239120.png" alt="" /></p>

<ul>
  <li>E 总资源</li>
  <li>P 已分配资源</li>
  <li>A 剩余资源</li>
</ul>

<p>算法详情：</p>

<ol>
  <li>查找右边矩阵中是否有一行，其未被满足的资源数均小于等于A。如果不存在这样的行，系统将死锁，因为任何进程都无法运行结束。</li>
  <li>若找到这样一行，则可以假设它获得所需的资源并运行结束，将该进程标为结束，并将资源加到向量 A 上。</li>
  <li>重复以上两步，直到所有的进程都标记为结束。若能达到这种状态，则初始状态是安全的；或者直到发生死锁。</li>
</ol>

<blockquote>
  <p>📌事实上，银行家算法虽然很有意义，但缺乏实用价值，因为很少有进程能在运行前知道自己所需资源的最大值！</p>
</blockquote>]]></content><author><name>zhanlutuzi</name></author><category term="笔记" /><category term="操作系统" /><category term="笔记" /><summary type="html"><![CDATA[I/O 硬件原理]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220302200521587.png" /><media:content medium="image" url="https://raw.githubusercontent.com/zhanlutuzi/imageBed/main/image/image-20220302200521587.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>